diff --git a/CHANGELOG.md b/CHANGELOG.md index 27d6d88e..f96fbd80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,6 +88,25 @@ Events that tend to reoccur very frequently (such as the ones that occur when yo A long requested feature was the availability of block place and break flags. These newly added flags work in tandem with the `BUILD` flag but can override `BUILD`. At the moment, it is not possible to allow explicit types of blocks to be placed or broken yet. +### Build permission nodes + +A new optional (disabled by default in the configuration) feature is the checking of build permission nodes. For every block, entity, and item, the following permission nodes are checked: + +* Block place: `worldguard.build.block.place.` +* Block break: `worldguard.build.block.remove.` +* Block interact: `worldguard.build.block.interact.` +* Entity spawn: `worldguard.build.block.place.` +* Entity destroy: `worldguard.build.block.remove.` +* Entity interact: `worldguard.build.block.interact.` +* Entity damage: `worldguard.build.block.damage.` +* Item use: `worldguard.build.item.interact.` + +In addition, the permissions are also checked in the style of `worldguard.build.block..`, so `worldguard.build.block..place` would work too. + +The list of usable material names comes from the [Material enumeration in Bukkit](http://jd.bukkit.org/rb/apidocs/org/bukkit/Material.html). For example, the permission for placing the bed block would be `worldguard.build.build.place.bed_block`. Be aware that _Material_ contains both item and block names. + +For entity names, see [EntityType](http://jd.bukkit.org/rb/apidocs/org/bukkit/entity/EntityType.html). + ### Other changes * Added a setting to permit "fake players" to bypass protection. Fake players are used by some servers and some mods to allow events to be thrown for mod blocks. WorldGuard considers a player to be a fake player if the name starts with `[` and ends with `]`. diff --git a/UPGRADE.md b/UPGRADE.md index 9300832a..ca38828e 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -104,6 +104,25 @@ Events that tend to reoccur very frequently (such as the ones that occur when yo A long requested feature was the availability of block place and break flags. These newly added flags work in tandem with the `BUILD` flag but can override `BUILD`. At the moment, it is not possible to allow explicit types of blocks to be placed or broken yet. +### Build permission nodes + +A new optional (disabled by default in the configuration) feature is the checking of build permission nodes. For every block, entity, and item, the following permission nodes are checked: + +* Block place: `worldguard.build.block.place.` +* Block break: `worldguard.build.block.remove.` +* Block interact: `worldguard.build.block.interact.` +* Entity spawn: `worldguard.build.block.place.` +* Entity destroy: `worldguard.build.block.remove.` +* Entity interact: `worldguard.build.block.interact.` +* Entity damage: `worldguard.build.block.damage.` +* Item use: `worldguard.build.item.interact.` + +In addition, the permissions are also checked in the style of `worldguard.build.block..`, so `worldguard.build.block..place` would work too. + +The list of usable material names comes from the [Material enumeration in Bukkit](http://jd.bukkit.org/rb/apidocs/org/bukkit/Material.html). For example, the permission for placing the bed block would be `worldguard.build.build.place.bed_block`. Be aware that _Material_ contains both item and block names. + +For entity names, see [EntityType](http://jd.bukkit.org/rb/apidocs/org/bukkit/entity/EntityType.html). + # Other changes The rest of the changes can be found in the CHANGELOG file. \ No newline at end of file diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldConfiguration.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldConfiguration.java index 992a5868..466b1c00 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldConfiguration.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldConfiguration.java @@ -26,6 +26,7 @@ import com.sk89q.worldguard.blacklist.logger.ConsoleHandler; import com.sk89q.worldguard.blacklist.logger.DatabaseHandler; import com.sk89q.worldguard.blacklist.logger.FileHandler; +import com.sk89q.worldguard.bukkit.commands.CommandUtils; import com.sk89q.worldguard.chest.ChestProtection; import com.sk89q.worldguard.chest.SignChestProtection; import org.bukkit.block.Block; @@ -79,6 +80,8 @@ public class WorldConfiguration { /* Configuration data start */ public boolean summaryOnStart; public boolean opPermissions; + public boolean buildPermissions; + public String buildPermissionDenyMessage = ""; public boolean fireSpreadDisableToggle; public boolean itemDurability; public boolean simulateSponge; @@ -311,6 +314,9 @@ private void loadConfiguration() { summaryOnStart = getBoolean("summary-on-start", true); opPermissions = getBoolean("op-permissions", true); + buildPermissions = getBoolean("build-permission-nodes.enable", false); + buildPermissionDenyMessage = CommandUtils.replaceColorMacros( + getString("build-permission-nodes.deny-message", "&eSorry, but you are not permitted to do that here.")); itemDurability = getBoolean("protection.item-durability", true); removeInfiniteStacks = getBoolean("protection.remove-infinite-stacks", false); diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java index 680f82d9..08dbcd24 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java @@ -47,6 +47,7 @@ import com.sk89q.worldguard.bukkit.event.player.ProcessPlayerEvent; import com.sk89q.worldguard.bukkit.listener.BlacklistListener; import com.sk89q.worldguard.bukkit.listener.BlockedPotionsListener; +import com.sk89q.worldguard.bukkit.listener.BuildPermissionListener; import com.sk89q.worldguard.bukkit.listener.ChestProtectionListener; import com.sk89q.worldguard.bukkit.listener.DebuggingListener; import com.sk89q.worldguard.bukkit.listener.EventAbstractionListener; @@ -239,6 +240,7 @@ public void run() { (new BlockedPotionsListener(this)).registerEvents(); (new EventAbstractionListener(this)).registerEvents(); (new PlayerModesListener(this)).registerEvents(); + (new BuildPermissionListener(this)).registerEvents(); if ("true".equalsIgnoreCase(System.getProperty("worldguard.debug.listener"))) { (new DebuggingListener(this, log)).registerEvents(); } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/BuildPermissionListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/BuildPermissionListener.java new file mode 100644 index 00000000..26f88b98 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/BuildPermissionListener.java @@ -0,0 +1,204 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.listener; + +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.bukkit.event.block.BreakBlockEvent; +import com.sk89q.worldguard.bukkit.event.block.PlaceBlockEvent; +import com.sk89q.worldguard.bukkit.event.block.UseBlockEvent; +import com.sk89q.worldguard.bukkit.event.entity.DamageEntityEvent; +import com.sk89q.worldguard.bukkit.event.entity.DestroyEntityEvent; +import com.sk89q.worldguard.bukkit.event.entity.SpawnEntityEvent; +import com.sk89q.worldguard.bukkit.event.entity.UseEntityEvent; +import com.sk89q.worldguard.bukkit.event.inventory.UseItemEvent; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; + +public class BuildPermissionListener extends AbstractListener { + + /** + * Construct the listener. + * + * @param plugin an instance of WorldGuardPlugin + */ + public BuildPermissionListener(WorldGuardPlugin plugin) { + super(plugin); + } + + private boolean hasBuildPermission(CommandSender sender, String perm) { + return getPlugin().hasPermission(sender, "worldguard.build." + perm); + } + + private void tellErrorMessage(CommandSender sender, World world) { + String message = getWorldConfig(world).buildPermissionDenyMessage; + if (!message.isEmpty()) { + sender.sendMessage(message); + } + } + + @EventHandler(ignoreCancelled = true) + public void onPlaceBlock(final PlaceBlockEvent event) { + if (!getWorldConfig(event.getWorld()).buildPermissions) return; + + Object rootCause = event.getCause().getRootCause(); + + if (rootCause instanceof Player) { + final Player player = (Player) rootCause; + final Material material = event.getEffectiveMaterial(); + + if (!hasBuildPermission(player, "block." + material.name().toLowerCase() + ".place") + && !hasBuildPermission(player, "block.place." + material.name().toLowerCase())) { + tellErrorMessage(player, event.getWorld()); + event.setCancelled(true); + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onBreakBlock(final BreakBlockEvent event) { + if (!getWorldConfig(event.getWorld()).buildPermissions) return; + + Object rootCause = event.getCause().getRootCause(); + + if (rootCause instanceof Player) { + final Player player = (Player) rootCause; + final Material material = event.getEffectiveMaterial(); + + if (!hasBuildPermission(player, "block." + material.name().toLowerCase() + ".remove") + && !hasBuildPermission(player, "block.remove." + material.name().toLowerCase())) { + tellErrorMessage(player, event.getWorld()); + event.setCancelled(true); + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onUseBlock(final UseBlockEvent event) { + if (!getWorldConfig(event.getWorld()).buildPermissions) return; + + Object rootCause = event.getCause().getRootCause(); + + if (rootCause instanceof Player) { + final Player player = (Player) rootCause; + final Material material = event.getEffectiveMaterial(); + + if (!hasBuildPermission(player, "block." + material.name().toLowerCase() + ".interact") + && !hasBuildPermission(player, "block.interact." + material.name().toLowerCase())) { + tellErrorMessage(player, event.getWorld()); + event.setCancelled(true); + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onSpawnEntity(SpawnEntityEvent event) { + if (!getWorldConfig(event.getWorld()).buildPermissions) return; + + Object rootCause = event.getCause().getRootCause(); + + if (rootCause instanceof Player) { + final Player player = (Player) rootCause; + final EntityType type = event.getEffectiveType(); + + if (!hasBuildPermission(player, "entity." + type.name().toLowerCase() + ".place") + && !hasBuildPermission(player, "entity.place." + type.name().toLowerCase())) { + tellErrorMessage(player, event.getWorld()); + event.setCancelled(true); + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onDestroyEntity(DestroyEntityEvent event) { + if (!getWorldConfig(event.getWorld()).buildPermissions) return; + + Object rootCause = event.getCause().getRootCause(); + + if (rootCause instanceof Player) { + final Player player = (Player) rootCause; + final EntityType type = event.getEntity().getType(); + + if (!hasBuildPermission(player, "entity." + type.name().toLowerCase() + ".remove") + && !hasBuildPermission(player, "entity.remove." + type.name().toLowerCase())) { + tellErrorMessage(player, event.getWorld()); + event.setCancelled(true); + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onUseEntity(UseEntityEvent event) { + if (!getWorldConfig(event.getWorld()).buildPermissions) return; + + Object rootCause = event.getCause().getRootCause(); + + if (rootCause instanceof Player) { + final Player player = (Player) rootCause; + final EntityType type = event.getEntity().getType(); + + if (!hasBuildPermission(player, "entity." + type.name().toLowerCase() + ".interact") + && !hasBuildPermission(player, "entity.interact." + type.name().toLowerCase())) { + tellErrorMessage(player, event.getWorld()); + event.setCancelled(true); + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onDamageEntity(DamageEntityEvent event) { + if (!getWorldConfig(event.getWorld()).buildPermissions) return; + + Object rootCause = event.getCause().getRootCause(); + + if (rootCause instanceof Player) { + final Player player = (Player) rootCause; + final EntityType type = event.getEntity().getType(); + + if (!hasBuildPermission(player, "entity." + type.name().toLowerCase() + ".damage") + && !hasBuildPermission(player, "entity.damage." + type.name().toLowerCase())) { + tellErrorMessage(player, event.getWorld()); + event.setCancelled(true); + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onUseItem(UseItemEvent event) { + if (!getWorldConfig(event.getWorld()).buildPermissions) return; + + Object rootCause = event.getCause().getRootCause(); + + if (rootCause instanceof Player) { + Player player = (Player) rootCause; + Material material = event.getItemStack().getType(); + + if (!hasBuildPermission(player, "item." + material.name().toLowerCase() + ".interact") + || hasBuildPermission(player, "item.interact." + material.name().toLowerCase())) { + tellErrorMessage(player, event.getWorld()); + event.setCancelled(true); + } + } + } + +}