diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/BlockEventHandler.java b/src/main/java/me/ryanhamshire/GriefPrevention/BlockEventHandler.java index 5768dad..9a10e16 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/BlockEventHandler.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/BlockEventHandler.java @@ -76,6 +76,7 @@ import java.util.List; import java.util.Objects; import java.util.UUID; import java.util.function.BiPredicate; +import java.util.function.Supplier; //event handlers related to blocks public class BlockEventHandler implements Listener @@ -271,12 +272,12 @@ public class BlockEventHandler implements Listener if (claim != null) { playerData.lastClaim = claim; - String noContainerReason = claim.allowContainers(player); + Supplier noContainerReason = claim.checkPermission(player, ClaimPermission.Inventory, placeEvent); if (noContainerReason == null) return; placeEvent.setCancelled(true); - GriefPrevention.sendMessage(player, TextMode.Err, noContainerReason); + GriefPrevention.sendMessage(player, TextMode.Err, noContainerReason.get()); return; } } @@ -304,7 +305,7 @@ public class BlockEventHandler implements Listener } //if the player has permission for the claim and he's placing UNDER the claim - if (block.getY() <= claim.lesserBoundaryCorner.getBlockY() && claim.allowBuild(player, block.getType()) == null) + if (block.getY() <= claim.lesserBoundaryCorner.getBlockY() && claim.checkPermission(player, ClaimPermission.Build, placeEvent) == null) { //extend the claim downward this.dataStore.extendClaim(claim, block.getY() - GriefPrevention.instance.config_claims_claimsExtendIntoGroundDistance); @@ -755,7 +756,7 @@ public class BlockEventHandler implements Listener ProjectileSource shooter = ((Projectile) igniteEvent.getIgnitingEntity()).getShooter(); // Allow ignition if arrow was shot by a player with build permission. - if (shooter instanceof Player && claim.allowBuild((Player) shooter, Material.TNT) == null) return; + if (shooter instanceof Player && claim.checkPermission((Player) shooter, ClaimPermission.Build, igniteEvent) == null) return; // Allow ignition if arrow was shot by a dispenser in the same claim. if (shooter instanceof BlockProjectileSource && @@ -945,13 +946,13 @@ public class BlockEventHandler implements Listener return; } - String allowContainer = claim.allowContainers(shooter); + Supplier allowContainer = claim.checkPermission(shooter, ClaimPermission.Inventory, event); if (allowContainer != null) { event.getHitBlock().setType(Material.AIR); Bukkit.getScheduler().runTask(GriefPrevention.instance, () -> event.getHitBlock().setBlockData(block.getBlockData())); - GriefPrevention.sendMessage(shooter, TextMode.Err, allowContainer); + GriefPrevention.sendMessage(shooter, TextMode.Err, allowContainer.get()); return; } } diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/Claim.java b/src/main/java/me/ryanhamshire/GriefPrevention/Claim.java index 20c201d..f4de4d8 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/Claim.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/Claim.java @@ -19,6 +19,8 @@ package me.ryanhamshire.GriefPrevention; import me.ryanhamshire.GriefPrevention.util.BoundingBox; +import me.ryanhamshire.GriefPrevention.events.ClaimPermissionCheckEvent; +import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Material; @@ -28,6 +30,11 @@ import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockEvent; +import org.bukkit.event.block.BlockPlaceEvent; import java.util.ArrayList; import java.util.Calendar; @@ -39,6 +46,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.UUID; +import java.util.function.Supplier; //represents a player claim //creating an instance doesn't make an effective claim @@ -95,9 +103,7 @@ public class Claim //administrative claims are created and maintained by players with the griefprevention.adminclaims permission. public boolean isAdminClaim() { - if (this.parent != null) return this.parent.isAdminClaim(); - - return (this.ownerID == null); + return this.getOwnerID() == null; } //accessor for ID @@ -119,7 +125,7 @@ public class Claim { if (this.isAdminClaim()) return false; - if (this.allowAccess(defender) != null) return false; + if (this.checkPermission(defender, ClaimPermission.Access, null) != null) return false; return true; } @@ -310,50 +316,15 @@ public class Claim return claim.contains(location, false, true); } - //permissions. note administrative "public" claims have different rules than other claims - //all of these return NULL when a player has permission, or a String error message when the player doesn't have permission + /** + * @deprecated Check {@link ClaimPermission#Edit} with {@link #checkPermission(Player, ClaimPermission, Event)}. + * @param player the Player + * @return the denial message, or null if the action is allowed + */ + @Deprecated public String allowEdit(Player player) { - //if we don't know who's asking, always say no (i've been told some mods can make this happen somehow) - if (player == null) return ""; - - //special cases... - - //admin claims need adminclaims permission only. - if (this.isAdminClaim()) - { - if (player.hasPermission("griefprevention.adminclaims")) return null; - } - - //anyone with deleteclaims permission can modify non-admin claims at any time - else - { - if (player.hasPermission("griefprevention.deleteclaims")) return null; - } - - //no resizing, deleting, and so forth while under siege - if (player.getUniqueId().equals(this.ownerID)) - { - if (this.siegeData != null) - { - return GriefPrevention.instance.dataStore.getMessage(Messages.NoModifyDuringSiege); - } - - //otherwise, owners can do whatever - return null; - } - - //permission inheritance for subdivisions - if (this.parent != null) - { - if (player.getUniqueId().equals(this.parent.ownerID)) - return null; - if (!inheritNothing) - return this.parent.allowEdit(player); - } - - //error message if all else fails - return GriefPrevention.instance.dataStore.getMessage(Messages.OnlyOwnersModifyClaims, this.getOwnerName()); + return checkPermission(player, ClaimPermission.Edit, null).get(); } private static final Set PLACEABLE_FARMING_BLOCKS = EnumSet.of( @@ -371,73 +342,53 @@ public class Claim return PLACEABLE_FARMING_BLOCKS.contains(material); } + /** + * @deprecated Check {@link ClaimPermission#Build} with {@link #checkPermission(Player, ClaimPermission, Event)}. + * @param player the Player + * @return the denial message, or null if the action is allowed + */ + @Deprecated //build permission check public String allowBuild(Player player, Material material) { - //if we don't know who's asking, always say no (i've been told some mods can make this happen somehow) - if (player == null) return ""; + return checkPermission(player, ClaimPermission.Build, new CompatBuildBreakEvent(material, false)).get(); + } - //when a player tries to build in a claim, if he's under siege, the siege may extend to include the new claim - GriefPrevention.instance.dataStore.tryExtendSiege(player, this); + public static class CompatBuildBreakEvent extends Event + { + private final Material material; + private final boolean isBreak; - //admin claims can always be modified by admins, no exceptions - if (this.isAdminClaim()) + private CompatBuildBreakEvent(Material material, boolean isBreak) { - if (player.hasPermission("griefprevention.adminclaims")) return null; + this.material = material; + this.isBreak = isBreak; } - //no building while under siege - if (this.siegeData != null) + public Material getMaterial() { - return GriefPrevention.instance.dataStore.getMessage(Messages.NoBuildUnderSiege, this.siegeData.attacker.getName()); + return material; } - //no building while in pvp combat - PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getUniqueId()); - if (playerData.inPvpCombat()) + public boolean isBreak() { - return GriefPrevention.instance.dataStore.getMessage(Messages.NoBuildPvP); + return isBreak; } - //owners can make changes, or admins with ignore claims mode enabled - if (player.getUniqueId().equals(this.ownerID) || GriefPrevention.instance.dataStore.getPlayerData(player.getUniqueId()).ignoreClaims) - return null; - - //anyone with explicit build permission can make changes - if (this.hasExplicitPermission(player, ClaimPermission.Build)) return null; - - //also everyone is a member of the "public", so check for public permission - if (ClaimPermission.Build.isGrantedBy(this.playerIDToClaimPermissionMap.get("public"))) return null; - - //allow for farming with /containertrust permission - if (this.allowContainers(player) == null) + @Override + public HandlerList getHandlers() { - //do allow for farming, if player has /containertrust permission - if (placeableForFarming(material)) - { - return null; - } + return new HandlerList(); } - //subdivision permission inheritance - if (this.parent != null) - { - if (player.getUniqueId().equals(this.parent.ownerID)) - return null; - if (!inheritNothing) - return this.parent.allowBuild(player, material); - } - - //failure message for all other cases - String reason = GriefPrevention.instance.dataStore.getMessage(Messages.NoBuildPermission, this.getOwnerName()); - if (player.hasPermission("griefprevention.ignoreclaims")) - reason += " " + GriefPrevention.instance.dataStore.getMessage(Messages.IgnoreClaimsAdvertisement); - - return reason; } public boolean hasExplicitPermission(UUID uuid, ClaimPermission level) { + if (uuid.equals(this.getOwnerID())) return true; + + if (level == ClaimPermission.Manage) return this.managers.contains(uuid.toString()); + return level.isGrantedBy(this.playerIDToClaimPermissionMap.get(uuid.toString())); } @@ -446,185 +397,224 @@ public class Claim // Check explicit ClaimPermission for UUID if (this.hasExplicitPermission(player.getUniqueId(), level)) return true; + // Special case managers - a separate list is used. + if (level == ClaimPermission.Manage) + { + for (String node : this.managers) + { + // Ensure valid permission format for permissions - [permission.node] + if (node.length() < 3 || node.charAt(0) != '[' || node.charAt(node.length() - 1) != ']') continue; + // Check if player has node + if (player.hasPermission(node.substring(1, node.length() - 1))) return true; + } + return false; + } + // Check permission-based ClaimPermission for (Map.Entry stringToPermission : this.playerIDToClaimPermissionMap.entrySet()) { String node = stringToPermission.getKey(); // Ensure valid permission format for permissions - [permission.node] - if (node.length() < 3 || node.charAt(0) != '[' || node.charAt(node.length() - 1) != ']') - { - continue; - } + if (node.length() < 3 || node.charAt(0) != '[' || node.charAt(node.length() - 1) != ']') continue; // Check if level is high enough and player has node if (level.isGrantedBy(stringToPermission.getValue()) && player.hasPermission(node.substring(1, node.length() - 1))) - { return true; - } } return false; } - //break permission check + /** + * Check whether or not a Player has a certain level of trust. + * + * @param player the Player being checked for permissions + * @param permission the ClaimPermission level required + * @param event the Event triggering the permission check + * @return the denial message or null if permission is granted + */ + public Supplier checkPermission(Player player, ClaimPermission permission, Event event) + { + return checkPermission(player, permission, event, null); + } + + /** + * Check whether or not a Player has a certain level of trust. For internal use; allows changing default message. + * + * @param player the Player being checked for permissions + * @param permission the ClaimPermission level required + * @param event the Event triggering the permission check + * @param denialOverride a message overriding the default denial for clarity + * @return the denial message or null if permission is granted + */ + Supplier checkPermission(Player player, ClaimPermission permission, Event event, Supplier denialOverride) + { + return callPermissionCheck(new ClaimPermissionCheckEvent(player, this, permission, event), denialOverride); + } + + /** + * Check whether or not a UUID has a certain level of trust. + * + * @param uuid the UUID being checked for permissions + * @param permission the ClaimPermission level required + * @param event the Event triggering the permission check + * @return the denial reason or null if permission is granted + */ + public Supplier checkPermission(UUID uuid, ClaimPermission permission, Event event) + { + return callPermissionCheck(new ClaimPermissionCheckEvent(uuid, this, permission, event), null); + } + + /** + * Helper method for calling a ClaimPermissionCheckEvent. + * + * @param event the ClaimPermissionCheckEvent to call + * @param denialOverride a message overriding the default denial for clarity + * @return the denial reason or null if permission is granted + */ + private Supplier callPermissionCheck(ClaimPermissionCheckEvent event, Supplier denialOverride) + { + // Set denial message (if any) using default behavior. + Supplier defaultDenial = getDefaultDenial(event.getCheckedPlayer(), event.getCheckedUUID(), + event.getRequiredPermission(), event.getTriggeringEvent()); + // If permission is denied and a clarifying override is provided, use override. + if (defaultDenial != null && denialOverride != null) { + defaultDenial = denialOverride; + } + + event.setDenialReason(defaultDenial); + + Bukkit.getPluginManager().callEvent(event); + + return event.getDenialReason(); + } + + /** + * Get the default reason for denial of a ClaimPermission. + * + * @param player the Player being checked for permissions + * @param uuid the UUID being checked for permissions + * @param permission the ClaimPermission required + * @param event the Event triggering the permission check + * @return the denial reason or null if permission is granted + */ + private Supplier getDefaultDenial(Player player, UUID uuid, ClaimPermission permission, Event event) + { + if (player != null) + { + // Admin claims need adminclaims permission only. + if (this.isAdminClaim()) + { + if (player.hasPermission("griefprevention.adminclaims")) return null; + } + + // Anyone with deleteclaims permission can edit non-admin claims at any time. + else if (permission == ClaimPermission.Edit && player.hasPermission("griefprevention.deleteclaims")) + return null; + } + + // Claim owner and admins in ignoreclaims mode have access. + if (uuid.equals(this.getOwnerID()) || GriefPrevention.instance.dataStore.getPlayerData(uuid).ignoreClaims) + return null; + + // Look for explicit individual permission. + if (player != null) + { + if (this.hasExplicitPermission(player, permission)) return null; + } + else + { + if (this.hasExplicitPermission(uuid, permission)) return null; + } + + // Check for public permission. + if (permission.isGrantedBy(this.playerIDToClaimPermissionMap.get("public"))) return null; + + // Special building-only rules. + if (permission == ClaimPermission.Build) + { + // No building while in PVP. + PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(uuid); + if (playerData.inPvpCombat()) + { + return () -> GriefPrevention.instance.dataStore.getMessage(Messages.NoBuildPvP); + } + + // Allow farming crops with container trust. + Material material = null; + if (event instanceof BlockBreakEvent || event instanceof BlockPlaceEvent) + material = ((BlockEvent) event).getBlock().getType(); + + if (material != null && placeableForFarming(material) + && this.getDefaultDenial(player, uuid, ClaimPermission.Inventory, event) == null) + return null; + } + + // Permission inheritance for subdivisions. + if (this.parent != null) + { + if (!inheritNothing) + return this.parent.getDefaultDenial(player, uuid, permission, event); + } + + // Catch-all error message for all other cases. + return () -> + { + String reason = GriefPrevention.instance.dataStore.getMessage(permission.getDenialMessage(), this.getOwnerName()); + if (player != null && player.hasPermission("griefprevention.ignoreclaims")) + reason += " " + GriefPrevention.instance.dataStore.getMessage(Messages.IgnoreClaimsAdvertisement); + return reason; + }; + } + + /** + * @deprecated Check {@link ClaimPermission#Build} with {@link #checkPermission(Player, ClaimPermission, Event)}. + * @param player the Player + * @return the denial message, or null if the action is allowed + */ + @Deprecated public String allowBreak(Player player, Material material) { - //if under siege, some blocks will be breakable - if (this.siegeData != null || this.doorsOpen) - { - //search for block type in list of breakable blocks - boolean breakable = GriefPrevention.instance.config_siege_blocks.contains(material); - - //custom error messages for siege mode - if (!breakable) - { - return GriefPrevention.instance.dataStore.getMessage(Messages.NonSiegeMaterial); - } - else if (player.getUniqueId().equals(this.ownerID)) - { - return GriefPrevention.instance.dataStore.getMessage(Messages.NoOwnerBuildUnderSiege); - } - else - { - return null; - } - } - - //if not under siege, build rules apply - return this.allowBuild(player, material); + return checkPermission(player, ClaimPermission.Build, new CompatBuildBreakEvent(material, true)).get(); } - //access permission check + /** + * @deprecated Check {@link ClaimPermission#Access} with {@link #checkPermission(Player, ClaimPermission, Event)}. + * @param player the Player + * @return the denial message, or null if the action is allowed + */ + @Deprecated public String allowAccess(Player player) { - //following a siege where the defender lost, the claim will allow everyone access for a time - if (this.doorsOpen) return null; - - //admin claims need adminclaims permission only. - if (this.isAdminClaim()) - { - if (player.hasPermission("griefprevention.adminclaims")) return null; - } - - //claim owner and admins in ignoreclaims mode have access - if (player.getUniqueId().equals(this.ownerID) || GriefPrevention.instance.dataStore.getPlayerData(player.getUniqueId()).ignoreClaims) - return null; - - //look for explicit individual access, inventory, or build permission - if (this.hasExplicitPermission(player, ClaimPermission.Access)) return null; - - //also check for public permission - if (ClaimPermission.Access.isGrantedBy(this.playerIDToClaimPermissionMap.get("public"))) return null; - - //permission inheritance for subdivisions - if (this.parent != null) - { - if (player.getUniqueId().equals(this.parent.ownerID)) - return null; - if (!inheritNothing) - return this.parent.allowAccess(player); - } - - //catch-all error message for all other cases - String reason = GriefPrevention.instance.dataStore.getMessage(Messages.NoAccessPermission, this.getOwnerName()); - if (player.hasPermission("griefprevention.ignoreclaims")) - reason += " " + GriefPrevention.instance.dataStore.getMessage(Messages.IgnoreClaimsAdvertisement); - return reason; + return checkPermission(player, ClaimPermission.Access, null).get(); } - //inventory permission check + /** + * @deprecated Check {@link ClaimPermission#Inventory} with {@link #checkPermission(Player, ClaimPermission, Event)}. + * @param player the Player + * @return the denial message, or null if the action is allowed + */ + @Deprecated public String allowContainers(Player player) { - //if we don't know who's asking, always say no (i've been told some mods can make this happen somehow) - if (player == null) return ""; - - //trying to access inventory in a claim may extend an existing siege to include this claim - GriefPrevention.instance.dataStore.tryExtendSiege(player, this); - - //if under siege, nobody accesses containers - if (this.siegeData != null) - { - return GriefPrevention.instance.dataStore.getMessage(Messages.NoContainersSiege, siegeData.attacker.getName()); - } - - //owner and administrators in ignoreclaims mode have access - if (player.getUniqueId().equals(this.ownerID) || GriefPrevention.instance.dataStore.getPlayerData(player.getUniqueId()).ignoreClaims) - return null; - - //admin claims need adminclaims permission only. - if (this.isAdminClaim()) - { - if (player.hasPermission("griefprevention.adminclaims")) return null; - } - - //check for explicit individual container or build permission - if (this.hasExplicitPermission(player, ClaimPermission.Inventory)) return null; - - //check for public container or build permission - if (ClaimPermission.Inventory.isGrantedBy(this.playerIDToClaimPermissionMap.get("public"))) return null; - - //permission inheritance for subdivisions - if (this.parent != null) - { - if (player.getUniqueId().equals(this.parent.ownerID)) - return null; - if (!inheritNothing) - return this.parent.allowContainers(player); - } - - //error message for all other cases - String reason = GriefPrevention.instance.dataStore.getMessage(Messages.NoContainersPermission, this.getOwnerName()); - if (player.hasPermission("griefprevention.ignoreclaims")) - reason += " " + GriefPrevention.instance.dataStore.getMessage(Messages.IgnoreClaimsAdvertisement); - return reason; + return checkPermission(player, ClaimPermission.Inventory, null).get(); } - //grant permission check, relatively simple + /** + * @deprecated Check {@link ClaimPermission#Manage} with {@link #checkPermission(Player, ClaimPermission, Event)}. + * @param player the Player + * @return the denial message, or null if the action is allowed + */ + @Deprecated public String allowGrantPermission(Player player) { - //if we don't know who's asking, always say no (i've been told some mods can make this happen somehow) - if (player == null) return ""; - - //anyone who can modify the claim can do this - if (this.allowEdit(player) == null) return null; - - //anyone who's in the managers (/PermissionTrust) list can do this - for (String managerID : this.managers) - { - if (managerID == null) continue; - if (player.getUniqueId().toString().equals(managerID)) return null; - - else if (managerID.startsWith("[") && managerID.endsWith("]")) - { - managerID = managerID.substring(1, managerID.length() - 1); - if (managerID.isEmpty()) continue; - if (player.hasPermission(managerID)) return null; - } - } - - //permission inheritance for subdivisions - if (this.parent != null) - { - if (player.getUniqueId().equals(this.parent.ownerID)) - return null; - if (!inheritNothing) - return this.parent.allowGrantPermission(player); - } - - //generic error message - String reason = GriefPrevention.instance.dataStore.getMessage(Messages.NoPermissionTrust, this.getOwnerName()); - if (player.hasPermission("griefprevention.ignoreclaims")) - reason += " " + GriefPrevention.instance.dataStore.getMessage(Messages.IgnoreClaimsAdvertisement); - return reason; + return checkPermission(player, ClaimPermission.Manage, null).get(); } public ClaimPermission getPermission(String playerID) { - if (playerID == null || playerID.isEmpty()) - { - return null; - } + if (playerID == null || playerID.isEmpty()) return null; return this.playerIDToClaimPermissionMap.get(playerID.toLowerCase()); } @@ -632,18 +622,22 @@ public class Claim //grants a permission for a player or the public public void setPermission(String playerID, ClaimPermission permissionLevel) { - if (playerID == null || playerID.isEmpty()) - { - return; - } + if (permissionLevel == ClaimPermission.Edit) throw new IllegalArgumentException("Cannot add editors!"); - this.playerIDToClaimPermissionMap.put(playerID.toLowerCase(), permissionLevel); + if (playerID == null || playerID.isEmpty()) return; + + if (permissionLevel == ClaimPermission.Manage) + this.managers.add(playerID.toLowerCase()); + else + this.playerIDToClaimPermissionMap.put(playerID.toLowerCase(), permissionLevel); } //revokes a permission for a player or the public public void dropPermission(String playerID) { - this.playerIDToClaimPermissionMap.remove(playerID.toLowerCase()); + playerID = playerID.toLowerCase(); + this.playerIDToClaimPermissionMap.remove(playerID); + this.managers.remove(playerID); for (Claim child : this.children) { diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/ClaimPermission.java b/src/main/java/me/ryanhamshire/GriefPrevention/ClaimPermission.java index 5b6321e..f32ed75 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/ClaimPermission.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/ClaimPermission.java @@ -18,12 +18,47 @@ package me.ryanhamshire.GriefPrevention; -//basic enum stuff +/** + * Enum representing the permissions available in a {@link Claim}. + */ public enum ClaimPermission { - Build, - Inventory, - Access; + /** + * ClaimPermission used for owner-based checks. Cannot be granted and grants all other permissions. + */ + Edit(Messages.OnlyOwnersModifyClaims), + /** + * ClaimPermission used for building checks. Grants {@link #Inventory} and {@link #Access}. + */ + Build(Messages.NoBuildPermission), + /** + * ClaimPermission used for inventory management checks. Grants {@link #Access}. + */ + Inventory(Messages.NoContainersPermission), + /** + * ClaimPermission used for basic access. + */ + Access(Messages.NoAccessPermission), + /** + * ClaimPermission that allows users to grant ClaimPermissions. Uses a separate track from normal + * permissions and does not grant any other permissions. + */ + Manage(Messages.NoPermissionTrust); + + private final Messages denialMessage; + + ClaimPermission(Messages messages) + { + this.denialMessage = messages; + } + + /** + * @return the {@link Messages Message} used when alerting a user that they lack the ClaimPermission + */ + public Messages getDenialMessage() + { + return denialMessage; + } /** * Check if a ClaimPermission is granted by another ClaimPermission. @@ -33,7 +68,8 @@ public enum ClaimPermission */ public boolean isGrantedBy(ClaimPermission other) { - // As this uses declaration order to compare, if trust levels are reordered this method must be rewritten. + if (other == Manage || this == Manage) return other == this || other == Edit; + // This uses declaration order to compare! If trust levels are reordered this method must be rewritten. return other != null && other.ordinal() <= this.ordinal(); } diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/DataStore.java b/src/main/java/me/ryanhamshire/GriefPrevention/DataStore.java index 2022916..73a5735 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/DataStore.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/DataStore.java @@ -1285,7 +1285,12 @@ public abstract class DataStore if (claim.isAdminClaim()) return; //player must have some level of permission to be sieged in a claim - if (claim.allowAccess(player) != null) return; + Claim currentClaim = claim; + while (!currentClaim.hasExplicitPermission(player, ClaimPermission.Access)) + { + if (currentClaim.parent == null) return; + currentClaim = currentClaim.parent; + } //otherwise extend the siege playerData.siegeData.claims.add(claim); diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/EntityEventHandler.java b/src/main/java/me/ryanhamshire/GriefPrevention/EntityEventHandler.java index c93af06..9e101ad 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/EntityEventHandler.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/EntityEventHandler.java @@ -102,6 +102,7 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.UUID; +import java.util.function.Supplier; //handles events related to entities public class EntityEventHandler implements Listener @@ -998,12 +999,12 @@ public class EntityEventHandler implements Listener } //otherwise player must have container trust in the claim - String failureReason = claim.allowBuild(attacker, Material.AIR); + Supplier failureReason = claim.checkPermission(attacker, ClaimPermission.Build, event); if (failureReason != null) { event.setCancelled(true); if (sendErrorMessagesToPlayers) - GriefPrevention.sendMessage(attacker, TextMode.Err, failureReason); + GriefPrevention.sendMessage(attacker, TextMode.Err, failureReason.get()); return; } } @@ -1135,7 +1136,19 @@ public class EntityEventHandler implements Listener //otherwise the player damaging the entity must have permission, unless it's a dog in a pvp world else if (!(event.getEntity().getWorld().getPVP() && event.getEntity().getType() == EntityType.WOLF)) { - String noContainersReason = claim.allowContainers(attacker); + Supplier override = null; + if (sendErrorMessagesToPlayers) + { + final Player finalAttacker = attacker; + override = () -> + { + String message = GriefPrevention.instance.dataStore.getMessage(Messages.NoDamageClaimedEntity, claim.getOwnerName()); + if (finalAttacker.hasPermission("griefprevention.ignoreclaims")) + message += " " + GriefPrevention.instance.dataStore.getMessage(Messages.IgnoreClaimsAdvertisement); + return message; + }; + } + Supplier noContainersReason = claim.checkPermission(attacker, ClaimPermission.Inventory, event, override); if (noContainersReason != null) { event.setCancelled(true); @@ -1147,10 +1160,7 @@ public class EntityEventHandler implements Listener if (sendErrorMessagesToPlayers) { - String message = GriefPrevention.instance.dataStore.getMessage(Messages.NoDamageClaimedEntity, claim.getOwnerName()); - if (attacker.hasPermission("griefprevention.ignoreclaims")) - message += " " + GriefPrevention.instance.dataStore.getMessage(Messages.IgnoreClaimsAdvertisement); - GriefPrevention.sendMessage(attacker, TextMode.Err, message); + GriefPrevention.sendMessage(attacker, TextMode.Err, noContainersReason.get()); } event.setCancelled(true); } @@ -1349,15 +1359,19 @@ public class EntityEventHandler implements Listener //otherwise the player damaging the entity must have permission else { - String noContainersReason = claim.allowContainers(attacker); + final Player finalAttacker = attacker; + Supplier override = () -> + { + String message = GriefPrevention.instance.dataStore.getMessage(Messages.NoDamageClaimedEntity, claim.getOwnerName()); + if (finalAttacker.hasPermission("griefprevention.ignoreclaims")) + message += " " + GriefPrevention.instance.dataStore.getMessage(Messages.IgnoreClaimsAdvertisement); + return message; + }; + Supplier noContainersReason = claim.checkPermission(attacker, ClaimPermission.Inventory, event, override); if (noContainersReason != null) { event.setCancelled(true); - String message = GriefPrevention.instance.dataStore.getMessage(Messages.NoDamageClaimedEntity, claim.getOwnerName()); - if (attacker.hasPermission("griefprevention.ignoreclaims")) - message += " " + GriefPrevention.instance.dataStore.getMessage(Messages.IgnoreClaimsAdvertisement); - GriefPrevention.sendMessage(attacker, TextMode.Err, message); - event.setCancelled(true); + GriefPrevention.sendMessage(attacker, TextMode.Err, noContainersReason.get()); } //cache claim for later @@ -1399,10 +1413,12 @@ public class EntityEventHandler implements Listener if (claim != null) { cachedClaim = claim; - if (thrower == null || claim.allowContainers(thrower) != null) + Supplier override = () -> instance.dataStore.getMessage(Messages.NoDamageClaimedEntity, claim.getOwnerName()); + final Supplier noContainersReason = claim.checkPermission(thrower, ClaimPermission.Inventory, event, override); + if (thrower == null || noContainersReason != null) { event.setIntensity(effected, 0); - GriefPrevention.sendMessage(thrower, TextMode.Err, Messages.NoDamageClaimedEntity, claim.getOwnerName()); + GriefPrevention.sendMessage(thrower, TextMode.Err, noContainersReason.get()); return; } } diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/EquipShovelProcessingTask.java b/src/main/java/me/ryanhamshire/GriefPrevention/EquipShovelProcessingTask.java index d351c7c..76594e1 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/EquipShovelProcessingTask.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/EquipShovelProcessingTask.java @@ -70,7 +70,7 @@ class EquipShovelProcessingTask implements Runnable //if standing in a claim owned by the player, visualize it Claim claim = GriefPrevention.instance.dataStore.getClaimAt(player.getLocation(), true, playerData.lastClaim); - if (claim != null && claim.allowEdit(player) == null) + if (claim != null && claim.checkPermission(player, ClaimPermission.Edit, null) == null) { playerData.lastClaim = claim; Visualization.Apply(player, Visualization.FromClaim(claim, player.getEyeLocation().getBlockY(), VisualizationType.Claim, player.getLocation())); diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/GriefPrevention.java b/src/main/java/me/ryanhamshire/GriefPrevention/GriefPrevention.java index 83b7b07..2c1c126 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/GriefPrevention.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/GriefPrevention.java @@ -45,6 +45,7 @@ import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; @@ -64,6 +65,7 @@ import java.util.Set; import java.util.UUID; import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -359,6 +361,10 @@ public class GriefPrevention extends JavaPlugin EntityEventHandler entityEventHandler = new EntityEventHandler(this.dataStore, this); pluginManager.registerEvents(entityEventHandler, this); + //siege events + SiegeEventHandler siegeEventHandler = new SiegeEventHandler(); + pluginManager.registerEvents(siegeEventHandler, this); + //vault-based economy integration economyHandler = new EconomyHandler(this); pluginManager.registerEvents(economyHandler, this); @@ -1153,7 +1159,7 @@ public class GriefPrevention extends JavaPlugin } //must have permission to edit the land claim you're in - String errorMessage = claim.allowEdit(player); + Supplier errorMessage = claim.checkPermission(player, ClaimPermission.Edit, null); if (errorMessage != null) { GriefPrevention.sendMessage(player, TextMode.Err, Messages.NotYourClaim); @@ -1436,10 +1442,10 @@ public class GriefPrevention extends JavaPlugin } //if no permission to manage permissions, error message - String errorMessage = claim.allowGrantPermission(player); + Supplier errorMessage = claim.checkPermission(player, ClaimPermission.Manage, null); if (errorMessage != null) { - GriefPrevention.sendMessage(player, TextMode.Err, errorMessage); + GriefPrevention.sendMessage(player, TextMode.Err, errorMessage.get()); return true; } @@ -1528,7 +1534,7 @@ public class GriefPrevention extends JavaPlugin OfflinePlayer otherPlayer = null; if (args[0].equals("all")) { - if (claim == null || claim.allowEdit(player) == null) + if (claim == null || claim.checkPermission(player, ClaimPermission.Edit, null) == null) { clearPermissions = true; } @@ -1616,7 +1622,7 @@ public class GriefPrevention extends JavaPlugin } //otherwise, apply changes to only this claim - else if (claim.allowGrantPermission(player) != null) + else if (claim.checkPermission(player, ClaimPermission.Manage, null) != null) { GriefPrevention.sendMessage(player, TextMode.Err, Messages.NoPermissionTrust, claim.getOwnerName()); return true; @@ -1627,7 +1633,7 @@ public class GriefPrevention extends JavaPlugin if (clearPermissions) { //requires owner - if (claim.allowEdit(player) != null) + if (claim.checkPermission(player, ClaimPermission.Edit, null) != null) { GriefPrevention.sendMessage(player, TextMode.Err, Messages.UntrustAllOwnerOnly); return true; @@ -1655,7 +1661,7 @@ public class GriefPrevention extends JavaPlugin idToDrop = otherPlayer.getUniqueId().toString(); } boolean targetIsManager = claim.managers.contains(idToDrop); - if (targetIsManager && claim.allowEdit(player) != null) //only claim owners can untrust managers + if (targetIsManager && claim.checkPermission(player, ClaimPermission.Edit, null) != null) //only claim owners can untrust managers { GriefPrevention.sendMessage(player, TextMode.Err, Messages.ManagersDontUntrustManagers, claim.getOwnerName()); return true; @@ -2014,10 +2020,10 @@ public class GriefPrevention extends JavaPlugin } else { - String noBuildReason = claim.allowBuild(player, Material.STONE); + Supplier noBuildReason = claim.checkPermission(player, ClaimPermission.Build, null); if (noBuildReason != null) { - GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason); + GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason.get()); return true; } @@ -2423,7 +2429,7 @@ public class GriefPrevention extends JavaPlugin } //if the player isn't in a claim or has permission to build, tell him to man up - if (claim == null || claim.allowBuild(player, Material.AIR) == null) + if (claim == null || claim.checkPermission(player, ClaimPermission.Build, null) == null) { GriefPrevention.sendMessage(player, TextMode.Err, Messages.NotTrappedHere); return true; @@ -2547,7 +2553,7 @@ public class GriefPrevention extends JavaPlugin Claim defenderClaim = this.dataStore.getClaimAt(defender.getLocation(), false, null); //defender must have some level of permission there to be protected - if (defenderClaim == null || defenderClaim.allowAccess(defender) != null) + if (defenderClaim == null || defenderClaim.checkPermission(defender, ClaimPermission.Access, null) != null) { GriefPrevention.sendMessage(player, TextMode.Err, Messages.NotSiegableThere); return true; @@ -2876,7 +2882,7 @@ public class GriefPrevention extends JavaPlugin } //verify ownership - else if (claim.allowEdit(player) != null) + else if (claim.checkPermission(player, ClaimPermission.Edit, null) != null) { GriefPrevention.sendMessage(player, TextMode.Err, Messages.NotYourClaim); } @@ -2974,39 +2980,29 @@ public class GriefPrevention extends JavaPlugin else { //check permission here - if (claim.allowGrantPermission(player) != null) + if (claim.checkPermission(player, ClaimPermission.Manage, null) != null) { GriefPrevention.sendMessage(player, TextMode.Err, Messages.NoPermissionTrust, claim.getOwnerName()); return; } //see if the player has the level of permission he's trying to grant - String errorMessage = null; + Supplier errorMessage; //permission level null indicates granting permission trust if (permissionLevel == null) { - errorMessage = claim.allowEdit(player); + errorMessage = claim.checkPermission(player, ClaimPermission.Edit, null); if (errorMessage != null) { - errorMessage = "Only " + claim.getOwnerName() + " can grant /PermissionTrust here."; + errorMessage = () -> "Only " + claim.getOwnerName() + " can grant /PermissionTrust here."; } } //otherwise just use the ClaimPermission enum values else { - switch (permissionLevel) - { - case Access: - errorMessage = claim.allowAccess(player); - break; - case Inventory: - errorMessage = claim.allowContainers(player); - break; - default: - errorMessage = claim.allowBuild(player, Material.AIR); - } + errorMessage = claim.checkPermission(player, permissionLevel, null); } //error message for trying to grant a permission the player doesn't have @@ -3385,6 +3381,7 @@ public class GriefPrevention extends JavaPlugin public String allowBuild(Player player, Location location) { + // TODO check all derivatives and rework API return this.allowBuild(player, location, location.getBlock().getType()); } @@ -3431,20 +3428,27 @@ public class GriefPrevention extends JavaPlugin { //cache the claim for later reference playerData.lastClaim = claim; - return claim.allowBuild(player, material); + Block block = location.getBlock(); + + Supplier supplier = claim.checkPermission(player, ClaimPermission.Build, new BlockPlaceEvent(block, block.getState(), block, new ItemStack(material), player, true, EquipmentSlot.HAND)); + + if (supplier == null) return null; + + return supplier.get(); } } public String allowBreak(Player player, Block block, Location location) { - return this.allowBreak(player, block, location, null); - } - - public String allowBreak(Player player, Block block, Location location, BlockBreakEvent breakEvent) { - return this.allowBreak(player, block.getType(), location, breakEvent); + return this.allowBreak(player, block, location, new BlockBreakEvent(block, player)); } public String allowBreak(Player player, Material material, Location location, BlockBreakEvent breakEvent) + { + return this.allowBreak(player, location.getBlock(), location, breakEvent); + } + + public String allowBreak(Player player, Block block, Location location, BlockBreakEvent breakEvent) { if (!GriefPrevention.instance.claimsEnabledForWorld(location.getWorld())) return null; @@ -3479,7 +3483,7 @@ public class GriefPrevention extends JavaPlugin playerData.lastClaim = claim; //if not in the wilderness, then apply claim rules (permissions, etc) - String cancel = claim.allowBreak(player, material); + Supplier cancel = claim.checkPermission(player, ClaimPermission.Build, breakEvent); if (cancel != null && breakEvent != null) { PreventBlockBreakEvent preventionEvent = new PreventBlockBreakEvent(breakEvent); @@ -3490,7 +3494,9 @@ public class GriefPrevention extends JavaPlugin } } - return cancel; + if (cancel == null) return null; + + return cancel.get(); } } diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java b/src/main/java/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java index 21fd937..b7a0c40 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java @@ -106,6 +106,7 @@ import java.util.List; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; import java.util.regex.Pattern; class PlayerEventHandler implements Listener @@ -530,10 +531,10 @@ class PlayerEventHandler implements Listener if (claim != null) { playerData.lastClaim = claim; - String reason = claim.allowAccess(player); + Supplier reason = claim.checkPermission(player, ClaimPermission.Access, event); if (reason != null) { - GriefPrevention.sendMessage(player, TextMode.Err, reason); + GriefPrevention.sendMessage(player, TextMode.Err, reason.get()); event.setCancelled(true); } } @@ -1091,10 +1092,10 @@ class PlayerEventHandler implements Listener if (toClaim != null) { playerData.lastClaim = toClaim; - String noAccessReason = toClaim.allowAccess(player); + Supplier noAccessReason = toClaim.checkPermission(player, ClaimPermission.Access, event); if (noAccessReason != null) { - GriefPrevention.sendMessage(player, TextMode.Err, noAccessReason); + GriefPrevention.sendMessage(player, TextMode.Err, noAccessReason.get()); event.setCancelled(true); if (cause == TeleportCause.ENDER_PEARL) player.getInventory().addItem(new ItemStack(Material.ENDER_PEARL)); @@ -1142,12 +1143,12 @@ class PlayerEventHandler implements Listener Player player = event.getPlayer(); PlayerData playerData = this.dataStore.getPlayerData(player.getUniqueId()); - Claim toClaim = this.dataStore.getClaimAt(player.getLocation(), false, playerData.lastClaim); - if (toClaim == null) + Claim claim = this.dataStore.getClaimAt(player.getLocation(), false, playerData.lastClaim); + if (claim == null) return; - playerData.lastClaim = toClaim; - if (toClaim.allowBuild(player, Material.AIR) == null) + playerData.lastClaim = claim; + if (claim.checkPermission(player, ClaimPermission.Build, event) == null) return; event.setCancelled(true); @@ -1291,10 +1292,10 @@ class PlayerEventHandler implements Listener //for storage entities, apply container rules (this is a potential theft) if (entity instanceof InventoryHolder) { - String noContainersReason = claim.allowContainers(player); + Supplier noContainersReason = claim.checkPermission(player, ClaimPermission.Inventory, event); if (noContainersReason != null) { - GriefPrevention.sendMessage(player, TextMode.Err, noContainersReason); + GriefPrevention.sendMessage(player, TextMode.Err, noContainersReason.get()); event.setCancelled(true); return; } @@ -1309,12 +1310,18 @@ class PlayerEventHandler implements Listener Claim claim = this.dataStore.getClaimAt(entity.getLocation(), false, null); if (claim != null) { - if (claim.allowContainers(player) != null) + Supplier override = () -> { String message = instance.dataStore.getMessage(Messages.NoDamageClaimedEntity, claim.getOwnerName()); if (player.hasPermission("griefprevention.ignoreclaims")) message += " " + instance.dataStore.getMessage(Messages.IgnoreClaimsAdvertisement); - GriefPrevention.sendMessage(player, TextMode.Err, message); + + return message; + }; + final Supplier noContainersReason = claim.checkPermission(player, ClaimPermission.Inventory, event, override); + if (noContainersReason != null) + { + GriefPrevention.sendMessage(player, TextMode.Err, noContainersReason.get()); event.setCancelled(true); return; } @@ -1329,11 +1336,11 @@ class PlayerEventHandler implements Listener Claim claim = this.dataStore.getClaimAt(entity.getLocation(), false, playerData.lastClaim); if (claim != null) { - String failureReason = claim.allowContainers(player); + Supplier failureReason = claim.checkPermission(player, ClaimPermission.Inventory, event); if (failureReason != null) { event.setCancelled(true); - GriefPrevention.sendMessage(player, TextMode.Err, failureReason); + GriefPrevention.sendMessage(player, TextMode.Err, failureReason.get()); return; } } @@ -1362,18 +1369,18 @@ class PlayerEventHandler implements Listener Claim claim = this.dataStore.getClaimAt(event.getEgg().getLocation(), false, playerData.lastClaim); //allow throw egg if player is in ignore claims mode - if (playerData.ignoreClaims) return; + if (playerData.ignoreClaims || claim == null) return; - if (claim != null && claim.allowContainers(player) != null) + Supplier failureReason = claim.checkPermission(player, ClaimPermission.Inventory, event); + if (failureReason != null) { - String message = this.instance.dataStore.getMessage(Messages.NoContainersPermission, claim.getOwnerName()); - + String reason = failureReason.get(); if (player.hasPermission("griefprevention.ignoreclaims")) { - message += " " + instance.dataStore.getMessage(Messages.IgnoreClaimsAdvertisement); + reason += " " + instance.dataStore.getMessage(Messages.IgnoreClaimsAdvertisement); } - GriefPrevention.sendMessage(player, TextMode.Err, message); + GriefPrevention.sendMessage(player, TextMode.Err, reason); //cancel the event by preventing hatching event.setHatching(false); @@ -1402,7 +1409,7 @@ class PlayerEventHandler implements Listener if (claim != null) { //if no permission, cancel - String errorMessage = claim.allowContainers(player); + Supplier errorMessage = claim.checkPermission(player, ClaimPermission.Inventory, event); if (errorMessage != null) { event.setCancelled(true); @@ -1676,7 +1683,7 @@ class PlayerEventHandler implements Listener { playerData.lastClaim = claim; - String noAccessReason = claim.allowBreak(player, clickedBlockType); + Supplier noAccessReason = claim.checkPermission(player, ClaimPermission.Build, event); if (noAccessReason != null) { event.setCancelled(true); @@ -1701,11 +1708,11 @@ class PlayerEventHandler implements Listener { playerData.lastClaim = claim; - String noBuildReason = claim.allowBuild(player, Material.AIR); + Supplier noBuildReason = claim.checkPermission(player, ClaimPermission.Build, event); if (noBuildReason != null) { event.setCancelled(true); - GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason); + GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason.get()); player.sendBlockChange(adjacentBlock.getLocation(), adjacentBlock.getType(), adjacentBlock.getData()); return; } @@ -1766,11 +1773,11 @@ class PlayerEventHandler implements Listener { playerData.lastClaim = claim; - String noContainersReason = claim.allowContainers(player); + Supplier noContainersReason = claim.checkPermission(player, ClaimPermission.Inventory, event); if (noContainersReason != null) { event.setCancelled(true); - GriefPrevention.sendMessage(player, TextMode.Err, noContainersReason); + GriefPrevention.sendMessage(player, TextMode.Err, noContainersReason.get()); return; } } @@ -1803,11 +1810,11 @@ class PlayerEventHandler implements Listener { playerData.lastClaim = claim; - String noAccessReason = claim.allowAccess(player); + Supplier noAccessReason = claim.checkPermission(player, ClaimPermission.Access, event); if (noAccessReason != null) { event.setCancelled(true); - GriefPrevention.sendMessage(player, TextMode.Err, noAccessReason); + GriefPrevention.sendMessage(player, TextMode.Err, noAccessReason.get()); return; } } @@ -1822,11 +1829,11 @@ class PlayerEventHandler implements Listener { playerData.lastClaim = claim; - String noAccessReason = claim.allowAccess(player); + Supplier noAccessReason = claim.checkPermission(player, ClaimPermission.Access, event); if (noAccessReason != null) { event.setCancelled(true); - GriefPrevention.sendMessage(player, TextMode.Err, noAccessReason); + GriefPrevention.sendMessage(player, TextMode.Err, noAccessReason.get()); return; } } @@ -1841,11 +1848,11 @@ class PlayerEventHandler implements Listener { playerData.lastClaim = claim; - String noContainerReason = claim.allowAccess(player); + Supplier noContainerReason = claim.checkPermission(player, ClaimPermission.Access, event); if (noContainerReason != null) { event.setCancelled(true); - GriefPrevention.sendMessage(player, TextMode.Err, noContainerReason); + GriefPrevention.sendMessage(player, TextMode.Err, noContainerReason.get()); return; } } @@ -1867,11 +1874,11 @@ class PlayerEventHandler implements Listener Claim claim = this.dataStore.getClaimAt(clickedBlock.getLocation(), false, playerData.lastClaim); if (claim != null) { - String noBuildReason = claim.allowBuild(player, clickedBlockType); + Supplier noBuildReason = claim.checkPermission(player, ClaimPermission.Build, event); if (noBuildReason != null) { event.setCancelled(true); - GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason); + GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason.get()); return; } } @@ -1927,10 +1934,10 @@ class PlayerEventHandler implements Listener Claim claim = this.dataStore.getClaimAt(clickedBlock.getLocation(), false, playerData.lastClaim); if (claim != null) { - String reason = claim.allowContainers(player); + Supplier reason = claim.checkPermission(player, ClaimPermission.Inventory, event); if (reason != null) { - GriefPrevention.sendMessage(player, TextMode.Err, reason); + GriefPrevention.sendMessage(player, TextMode.Err, reason.get()); event.setCancelled(true); } } @@ -1951,10 +1958,10 @@ class PlayerEventHandler implements Listener Claim claim = this.dataStore.getClaimAt(clickedBlock.getLocation(), false, playerData.lastClaim); if (claim != null) { - String reason = claim.allowContainers(player); + Supplier reason = claim.checkPermission(player, ClaimPermission.Inventory, event); if (reason != null) { - GriefPrevention.sendMessage(player, TextMode.Err, reason); + GriefPrevention.sendMessage(player, TextMode.Err, reason.get()); event.setCancelled(true); } } @@ -2380,7 +2387,7 @@ class PlayerEventHandler implements Listener if (claim != null) { //if the player has permission to edit the claim or subdivision - String noEditReason = claim.allowEdit(player); + Supplier noEditReason = claim.checkPermission(player, ClaimPermission.Edit, event, () -> instance.dataStore.getMessage(Messages.CreateClaimFailOverlapOtherPlayer, claim.getOwnerName())); if (noEditReason == null) { //if he clicked on a corner, start resizing it @@ -2481,7 +2488,7 @@ class PlayerEventHandler implements Listener //otherwise tell the player he can't claim here because it's someone else's claim, and show him the claim else { - GriefPrevention.sendMessage(player, TextMode.Err, Messages.CreateClaimFailOverlapOtherPlayer, claim.getOwnerName()); + GriefPrevention.sendMessage(player, TextMode.Err, noEditReason.get()); Visualization visualization = Visualization.FromClaim(claim, clickedBlock.getY(), VisualizationType.ErrorClaim, player.getLocation()); // alert plugins of a visualization @@ -2659,12 +2666,12 @@ class PlayerEventHandler implements Listener if (claim != null) { playerData.lastClaim = claim; - String noContainerReason = claim.allowContainers(player); + Supplier noContainerReason = claim.checkPermission(player, ClaimPermission.Inventory, event); if (noContainerReason != null) { event.setCancelled(true); player.closeInventory(); - GriefPrevention.sendMessage(player, TextMode.Err, noContainerReason); + GriefPrevention.sendMessage(player, TextMode.Err, noContainerReason.get()); } } } diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/SecureClaimTask.java b/src/main/java/me/ryanhamshire/GriefPrevention/SecureClaimTask.java index 488ca90..7fb8a23 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/SecureClaimTask.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/SecureClaimTask.java @@ -47,7 +47,7 @@ class SecureClaimTask implements Runnable Collection onlinePlayers = (Collection) GriefPrevention.instance.getServer().getOnlinePlayers(); for (Player player : onlinePlayers) { - if (claim.contains(player.getLocation(), false, false) && claim.allowAccess(player) != null) + if (claim.contains(player.getLocation(), false, false) && claim.checkPermission(player, ClaimPermission.Access, null) != null) { GriefPrevention.sendMessage(player, TextMode.Err, Messages.SiegeDoorsLockedEjection); GriefPrevention.instance.ejectPlayer(player); diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/SiegeCheckupTask.java b/src/main/java/me/ryanhamshire/GriefPrevention/SiegeCheckupTask.java index c215824..507ed6a 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/SiegeCheckupTask.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/SiegeCheckupTask.java @@ -20,6 +20,8 @@ package me.ryanhamshire.GriefPrevention; import org.bukkit.entity.Player; +import java.util.function.Supplier; + //checks to see whether or not a siege should end based on the locations of the players //for example, defender escaped or attacker gave up and left class SiegeCheckupTask implements Runnable @@ -44,7 +46,7 @@ class SiegeCheckupTask implements Runnable //if this is a new claim and he has some permission there, extend the siege to include it if (defenderClaim != null) { - String noAccessReason = defenderClaim.allowAccess(defender); + Supplier noAccessReason = defenderClaim.checkPermission(defender, ClaimPermission.Access, null); if (defenderClaim.canSiege(defender) && noAccessReason == null) { this.siegeData.claims.add(defenderClaim); diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/SiegeEventHandler.java b/src/main/java/me/ryanhamshire/GriefPrevention/SiegeEventHandler.java new file mode 100644 index 0000000..c06820a --- /dev/null +++ b/src/main/java/me/ryanhamshire/GriefPrevention/SiegeEventHandler.java @@ -0,0 +1,100 @@ +package me.ryanhamshire.GriefPrevention; + +import me.ryanhamshire.GriefPrevention.events.ClaimPermissionCheckEvent; +import org.bukkit.Material; +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.player.PlayerInteractEvent; + +public class SiegeEventHandler implements Listener +{ + + @EventHandler(priority = EventPriority.LOWEST) + public void onClaimPermissionCheck(ClaimPermissionCheckEvent event) + { + if (event.getRequiredPermission() == ClaimPermission.Manage) return; + + Player player = event.getCheckedPlayer(); + + // Player must be online to use siege features. + if (player == null) return; + + Claim claim = event.getClaim(); + + // Admin claims cannot be sieged. + if (claim.isAdminClaim()) return; + + // Claim modification during siege is not allowed. + if (event.getRequiredPermission() == ClaimPermission.Edit) + { + if (claim.siegeData != null) + event.setDenialReason(() -> GriefPrevention.instance.dataStore.getMessage(Messages.NoModifyDuringSiege)); + return; + } + + // Following a siege where the defender lost, the claim will allow everyone access for a time. + if (event.getRequiredPermission() == ClaimPermission.Access) + { + if (claim.doorsOpen) + event.setDenialReason(null); + return; + } + + // If under siege, nobody accesses containers. + if (event.getRequiredPermission() == ClaimPermission.Inventory) + { + // Trying to access inventory in a claim may extend an existing siege to include this claim. + GriefPrevention.instance.dataStore.tryExtendSiege(player, claim); + + if (claim.siegeData != null) + event.setDenialReason(() -> GriefPrevention.instance.dataStore.getMessage(Messages.NoContainersSiege, claim.siegeData.attacker.getName())); + + return; + } + + // When a player tries to build in a claim, if he's under siege, the siege may extend to include the new claim. + GriefPrevention.instance.dataStore.tryExtendSiege(player, claim); + + // If claim is not under siege and doors are not open, use default behavior. + if (claim.siegeData == null && !claim.doorsOpen) + return; + + // If under siege, some blocks will be breakable. + Material broken = null; + if (event.getTriggeringEvent() instanceof BlockBreakEvent) + broken = ((BlockBreakEvent) event.getTriggeringEvent()).getBlock().getType(); + else if (event.getTriggeringEvent() instanceof Claim.CompatBuildBreakEvent) + { + Claim.CompatBuildBreakEvent triggeringEvent = (Claim.CompatBuildBreakEvent) event.getTriggeringEvent(); + if (triggeringEvent.isBreak()) + broken = triggeringEvent.getMaterial(); + } + else if (event.getTriggeringEvent() instanceof PlayerInteractEvent) + { + PlayerInteractEvent triggeringEvent = (PlayerInteractEvent) event.getTriggeringEvent(); + if (triggeringEvent.getAction() == Action.PHYSICAL && triggeringEvent.getClickedBlock() != null + && triggeringEvent.getClickedBlock().getType() == Material.TURTLE_EGG) + broken = Material.TURTLE_EGG; + } + + if (broken != null) + { + // Error messages for siege mode. + if (!GriefPrevention.instance.config_siege_blocks.contains(broken)) + event.setDenialReason(() -> GriefPrevention.instance.dataStore.getMessage(Messages.NonSiegeMaterial)); + else if (player.getUniqueId().equals(claim.ownerID)) + event.setDenialReason(() -> GriefPrevention.instance.dataStore.getMessage(Messages.NoOwnerBuildUnderSiege)); + return; + } + + // No building while under siege. + if (claim.siegeData != null) + event.setDenialReason(() -> GriefPrevention.instance.dataStore.getMessage(Messages.NoBuildUnderSiege, claim.siegeData.attacker.getName())); + + } + +} diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/events/ClaimPermissionCheckEvent.java b/src/main/java/me/ryanhamshire/GriefPrevention/events/ClaimPermissionCheckEvent.java new file mode 100644 index 0000000..3bde918 --- /dev/null +++ b/src/main/java/me/ryanhamshire/GriefPrevention/events/ClaimPermissionCheckEvent.java @@ -0,0 +1,146 @@ +package me.ryanhamshire.GriefPrevention.events; + +import me.ryanhamshire.GriefPrevention.Claim; +import me.ryanhamshire.GriefPrevention.ClaimPermission; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +import java.util.UUID; +import java.util.function.Supplier; + +/** + * This event is called when a {@link Claim} requires a specific level of trust. + * If the denial reason is null, the trust requirements are met and the action is allowed. + */ +public class ClaimPermissionCheckEvent extends Event +{ + + private static final HandlerList handlers = new HandlerList(); + + private final Player checkedPlayer; + private final UUID checkedUUID; + private final Claim claim; + private final ClaimPermission requiredPermission; + private final Event triggeringEvent; + private Supplier denial; + + /** + * Constructor for a ClaimPermissionCheckEvent. + * + * @param checked the Player being checked for permissions + * @param claim the Claim in which permissions are being checked + * @param required the ClaimPermission level required + * @param triggeringEvent the Event triggering the permission check + */ + public ClaimPermissionCheckEvent(Player checked, Claim claim, ClaimPermission required, Event triggeringEvent) + { + this(checked, checked.getUniqueId(), claim, required, triggeringEvent); + } + + /** + * Constructor for a ClaimPermissionCheckEvent. + * + * @param checked the UUID being checked for permissions + * @param claim the Claim in which permissions are being checked + * @param required the ClaimPermission level required + * @param triggeringEvent the Event triggering the permission check + */ + public ClaimPermissionCheckEvent(UUID checked, Claim claim, ClaimPermission required, Event triggeringEvent) + { + this(Bukkit.getPlayer(checked), checked, claim, required, triggeringEvent); + } + + private ClaimPermissionCheckEvent(Player checkedPlayer, UUID checkedUUID, Claim claim, ClaimPermission required, Event triggeringEvent) + { + this.checkedPlayer = checkedPlayer; + this.checkedUUID = checkedUUID; + this.claim = claim; + this.requiredPermission = required; + this.triggeringEvent = triggeringEvent; + } + + /** + * Returns the Player being checked for permission if online. + * + * @return the Player being checked or null if offline + */ + public Player getCheckedPlayer() + { + return checkedPlayer; + } + + /** + * Returns the UUID being checked for permission. + * + * @return the UUID being checked for permission + */ + public UUID getCheckedUUID() + { + return checkedUUID; + } + + /** + * Returns the Claim in which permission is being checked. + * + * @return the Claim in which permission is being checked + */ + public Claim getClaim() + { + return claim; + } + + /** + * Returns the ClaimPermission being checked for. + * + * @return the ClaimPermission being checked for + */ + public ClaimPermission getRequiredPermission() + { + return requiredPermission; + } + + /** + * Returns the Event causing this event to fire. + * + * @return the Event triggering this event or null if none was provided + */ + public Event getTriggeringEvent() + { + return triggeringEvent; + } + + /** + * Returns the reason the ClaimPermission check failed. + * If the check did not fail, the message will be null. + * + * @return the denial reason or null if permission is granted + */ + public Supplier getDenialReason() + { + return denial; + } + + /** + * Sets the reason for denial. + * + * @param denial the denial reason + */ + public void setDenialReason(Supplier denial) + { + this.denial = denial; + } + + @Override + public HandlerList getHandlers() + { + return handlers; + } + + public static HandlerList getHandlerList() + { + return handlers; + } + +}