diff --git a/src/main/java/net/knarcraft/permissionsigns/PermissionSigns.java b/src/main/java/net/knarcraft/permissionsigns/PermissionSigns.java index cceea99..16b9679 100644 --- a/src/main/java/net/knarcraft/permissionsigns/PermissionSigns.java +++ b/src/main/java/net/knarcraft/permissionsigns/PermissionSigns.java @@ -42,6 +42,7 @@ public final class PermissionSigns extends JavaPlugin { private static String pluginVersion; private static PermissionSigns instance; private static boolean perWorldPermissions; + private static boolean enableExtensiveSignProtection; /** * Instantiates the permission signs class @@ -120,42 +121,60 @@ public final class PermissionSigns extends JavaPlugin { return perWorldPermissions; } + /** + * Gets whether permission signs on falling blocks should be protected + * + * @return

Whether permission signs on falling blocks should be protected

+ */ + public static boolean extensiveSignProtectionEnabled() { + return enableExtensiveSignProtection; + } + @Override public void reloadConfig() { super.reloadConfig(); - FileConfiguration config = this.getConfig(); - String language = config.getString("language"); - perWorldPermissions = config.getBoolean("perWorldPermissions"); - Translator.loadLanguages(language); + Translator.loadLanguages(loadConfig()); } @Override public void onEnable() { - PluginDescriptionFile pluginDescriptionFile = this.getDescription(); - pluginVersion = pluginDescriptionFile.getVersion(); - //TODO: Display a notice in the console if a new version is available - - FileConfiguration config = this.getConfig(); - config.options().copyDefaults(true); - this.saveDefaultConfig(); - - String language = config.getString("language"); - perWorldPermissions = config.getBoolean("perWorldPermissions"); - //Check if vault is loaded setupVault(); + //Get plugin info + PluginDescriptionFile pluginDescriptionFile = this.getDescription(); + pluginVersion = pluginDescriptionFile.getVersion(); + + //TODO: Display a notice in the console if a new version is available (requires Spigot resource first) + + //Load config and write the default config if necessary + FileConfiguration config = this.getConfig(); + config.options().copyDefaults(true); + this.saveDefaultConfig(); + Translator.loadLanguages(loadConfig()); + registerListeners(); - - Translator.loadLanguages(language); registerCommands(); - runThreads(); SignManager.loadSigns(); PermissionManager.loadTemporaryPermissions(); } + /** + * Loads the config file + * + * @return

The currently selected language

+ */ + private String loadConfig() { + FileConfiguration config = this.getConfig(); + String language = config.getString("language", "en"); + perWorldPermissions = config.getBoolean("perWorldPermissions", false); + enableExtensiveSignProtection = config.getBoolean("enableExtensiveSignProtection", false); + saveConfig(); + return language; + } + /** * Starts all separate threads for executing tasks */ diff --git a/src/main/java/net/knarcraft/permissionsigns/listener/BlockListener.java b/src/main/java/net/knarcraft/permissionsigns/listener/BlockListener.java index e54a49c..0c23a34 100644 --- a/src/main/java/net/knarcraft/permissionsigns/listener/BlockListener.java +++ b/src/main/java/net/knarcraft/permissionsigns/listener/BlockListener.java @@ -12,15 +12,28 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPhysicsEvent; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; + +import static net.knarcraft.permissionsigns.PermissionSigns.extensiveSignProtectionEnabled; /** * A listener for relevant block events such as signs being broken */ public class BlockListener implements Listener { + @EventHandler + public void onBlockPhysics(BlockPhysicsEvent event) { + //Block any physics events from destroying signs + if (SignManager.getSign(event.getBlock().getLocation()) != null) { + event.setCancelled(true); + } + } + @EventHandler public void onBlockBreak(BlockBreakEvent event) { Block block = event.getBlock(); @@ -30,14 +43,48 @@ public class BlockListener implements Listener { return; } - //if (block instanceof FallingBlock) { - //TODO: Search recursively upwards (and downwards if pointed dripstone) until a non-falling block is found. - // If upwards, check the non-falling block, but not downwards. If player does not have the create permission, - // just stop and cancel once a permission sign is found. If it has, all affected signs need to be - // de-registered. Must account for stacks of sand and signs on top of each-other. So if a normal sign is - // found, the recursion upward must also happen. - //} + //Protect the permission sign itself + protectBlockIfPermissionSign(event, block, player); + if (extensiveSignProtectionEnabled()) { + protectSignsInDirection(event, block, player, BlockFace.UP); + if (event.isCancelled()) { + return; + } + protectSignsInDirection(event, block, player, BlockFace.DOWN); + } + } + + /** + * Protects signs on falling blocks in the given direction + * + * @param event

The triggered block break event

+ * @param block

The broken block

+ * @param player

The player breaking the block

+ * @param direction

The direction to check for affected blocks

+ */ + private void protectSignsInDirection(BlockBreakEvent event, Block block, Player player, BlockFace direction) { + Block directionBlock = block; + do { + directionBlock = directionBlock.getRelative(direction); + protectBlockIfPermissionSign(event, directionBlock, player); + if (event.isCancelled()) { + return; + } + } while ((direction == BlockFace.DOWN && directionBlock.getBlockData().getMaterial() == + Material.POINTED_DRIPSTONE) || (direction == BlockFace.UP && + isAffectedByGravity(directionBlock.getBlockData().getMaterial()))); + } + + /** + * Protects the given sign + * + * @param event

The triggered block break event

+ * @param block

The block to check if it's a sign

+ * @param player

The player breaking the block

+ */ + private void protectBlockIfPermissionSign(BlockBreakEvent event, Block block, Player player) { + //Protect the permission sign itself Material material = block.getBlockData().getMaterial(); if (Tag.SIGNS.isTagged(material) || Tag.WALL_SIGNS.isTagged(material)) { checkIfBlockIsPermissionSign(block, player, event); @@ -46,34 +93,24 @@ public class BlockListener implements Listener { } } - Block relativeBlock = block.getRelative(BlockFace.UP); - if (Tag.SIGNS.isTagged(relativeBlock.getBlockData().getMaterial())) { - checkIfBlockIsPermissionSign(relativeBlock, player, event); - if (event.isCancelled()) { - return; - } - } + Map> blocksToCheck = new HashMap<>(); - if (block.getBlockData().getMaterial() == Material.POINTED_DRIPSTONE) { - relativeBlock = block.getRelative(BlockFace.DOWN); - if (Tag.WALL_SIGNS.isTagged(relativeBlock.getBlockData().getMaterial())) { - checkIfBlockIsPermissionSign(relativeBlock, player, event); - if (event.isCancelled()) { - return; - } - } - } + //Protect any block with a permission sign on it + blocksToCheck.put(block.getRelative(BlockFace.UP), Tag.SIGNS); + + //Protect any permission signs attached to the block for (BlockFace blockFace : getRelevantBlockFaces()) { - relativeBlock = block.getRelative(blockFace); - if (Tag.WALL_SIGNS.isTagged(relativeBlock.getBlockData().getMaterial())) { - checkIfBlockIsPermissionSign(relativeBlock, player, event); + blocksToCheck.put(block.getRelative(blockFace), Tag.WALL_SIGNS); + } + + for (Block blockToCheck : blocksToCheck.keySet()) { + if (blocksToCheck.get(blockToCheck).isTagged(blockToCheck.getBlockData().getMaterial())) { + checkIfBlockIsPermissionSign(blockToCheck, player, event); if (event.isCancelled()) { return; } } } - - //TODO: Need to protect against other things that might damage the sign, such as explosions } /** @@ -117,4 +154,23 @@ public class BlockListener implements Listener { return relevantBlockFaces; } + /** + * Checks whether the given material is affected by gravity + * + * @param material

The material to check

+ * @return

True if the material is affected by gravity

+ */ + private boolean isAffectedByGravity(Material material) { + return Tag.SAND.isTagged(material) || Tag.ANVIL.isTagged(material) || material == Material.POINTED_DRIPSTONE || + Tag.SIGNS.isTagged(material) || material == Material.DRAGON_EGG || material == Material.GRAVEL || + material == Material.BLACK_CONCRETE_POWDER || material == Material.BLUE_CONCRETE_POWDER || + material == Material.BROWN_CONCRETE_POWDER || material == Material.CYAN_CONCRETE_POWDER || + material == Material.LIME_CONCRETE_POWDER || material == Material.GRAY_CONCRETE_POWDER || + material == Material.GREEN_CONCRETE_POWDER || material == Material.LIGHT_BLUE_CONCRETE_POWDER || + material == Material.MAGENTA_CONCRETE_POWDER || material == Material.PINK_CONCRETE_POWDER || + material == Material.LIGHT_GRAY_CONCRETE_POWDER || material == Material.ORANGE_CONCRETE_POWDER || + material == Material.RED_CONCRETE_POWDER || material == Material.PURPLE_CONCRETE_POWDER || + material == Material.YELLOW_CONCRETE_POWDER || material == Material.WHITE_CONCRETE_POWDER; + } + } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 5c9c577..627c777 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -3,3 +3,7 @@ language: en # Whether to only give permissions for a single world, instead of granting permissions for all worlds perWorldPermissions: false + +# Whether to protect permission signs on falling blocks (sand, gravel, anvil, drip-stone, signs) by preventing breakage +# of the blocks that would cause a sign to be destroyed. +enableExtensiveSignProtection: false \ No newline at end of file