From 23c023a0271ef1148f6c923e4ab45adde370cfcc Mon Sep 17 00:00:00 2001 From: EpicKnarvik97 Date: Mon, 17 Jan 2022 18:42:15 +0100 Subject: [PATCH] Various improvements to the interface More time units are supported, which makes long temporary durations more readable Makes right-clicking a sign display a full description, and requires sneaking while clicking to perform the transaction Makes signs only be destroyed when sneaking Improves sign protection a bit, but improvements are required Prevents a creative player breaking a sign from also interacting with it --- .../permissionsigns/PermissionSigns.java | 4 + .../container/PermissionSign.java | 39 ++++++++- .../formatting/TranslatableMessage.java | 51 +++++++++++- .../formatting/Translator.java | 3 +- .../listener/BlockListener.java | 80 ++++++++++++++++++- .../listener/SignListener.java | 47 +++++++++-- .../permissionsigns/utility/FileHelper.java | 3 +- src/main/resources/strings.yml | 24 +++++- 8 files changed, 228 insertions(+), 23 deletions(-) diff --git a/src/main/java/net/knarcraft/permissionsigns/PermissionSigns.java b/src/main/java/net/knarcraft/permissionsigns/PermissionSigns.java index e9d9890..6da75b1 100644 --- a/src/main/java/net/knarcraft/permissionsigns/PermissionSigns.java +++ b/src/main/java/net/knarcraft/permissionsigns/PermissionSigns.java @@ -33,6 +33,9 @@ import java.util.Queue; import java.util.UUID; import java.util.stream.Stream; +/** + * The main permissionsigns class + */ public final class PermissionSigns extends JavaPlugin { private static final Queue signCreationRequests = new PriorityQueue<>(); @@ -131,6 +134,7 @@ public final class PermissionSigns extends JavaPlugin { PluginDescriptionFile pluginDescriptionFile = this.getDescription(); pluginVersion = pluginDescriptionFile.getVersion(); //TODO: Allow for custom language files. Perhaps just look for strings.yml in the folder + //TODO: Display a notice in the console if a new version is available FileConfiguration config = this.getConfig(); config.options().copyDefaults(true); diff --git a/src/main/java/net/knarcraft/permissionsigns/container/PermissionSign.java b/src/main/java/net/knarcraft/permissionsigns/container/PermissionSign.java index a24621f..4314b49 100644 --- a/src/main/java/net/knarcraft/permissionsigns/container/PermissionSign.java +++ b/src/main/java/net/knarcraft/permissionsigns/container/PermissionSign.java @@ -148,11 +148,34 @@ public class PermissionSign { * * @return

The string used for displaying this sign's duration

*/ - private String getDurationString() { + public String getDurationString() { if (duration == 0) { return Translator.getTranslatedMessage(TranslatableMessage.SIGN_PERMANENT); } else { - return duration + " " + Translator.getTranslatedMessage(TranslatableMessage.SIGN_TIME_UNIT); + double minute = 60; + double hour = minute * 60; + double day = hour * 24; + if (duration / day >= 1) { + double days = round(duration / day); + if (days == 1) { + return (int) days + " " + Translator.getTranslatedMessage(TranslatableMessage.UNIT_DAY); + } else { + return days + " " + Translator.getTranslatedMessage(TranslatableMessage.UNIT_DAYS); + } + } else if (duration / hour >= 1) { + double hours = round(duration / hour); + if (hours == 1) { + return (int) hours + " " + Translator.getTranslatedMessage(TranslatableMessage.UNIT_HOUR); + } else { + return hours + " " + Translator.getTranslatedMessage(TranslatableMessage.UNIT_HOURS); + } + } else { + if (duration == 1) { + return duration + " " + Translator.getTranslatedMessage(TranslatableMessage.UNIT_SECOND); + } else { + return duration + " " + Translator.getTranslatedMessage(TranslatableMessage.UNIT_SECONDS); + } + } } } @@ -161,7 +184,7 @@ public class PermissionSign { * * @return

The string used for displaying this sign's cost

*/ - private String getCostString() { + public String getCostString() { if (cost == 0) { return Translator.getTranslatedMessage(TranslatableMessage.SIGN_COST_FREE); } else { @@ -170,4 +193,14 @@ public class PermissionSign { } } + /** + * Rounds a number to its last two digits + * + * @param number

The number to round

+ * @return

The rounded number

+ */ + private double round(double number) { + return Math.round(number * 100.0) / 100.0; + } + } diff --git a/src/main/java/net/knarcraft/permissionsigns/formatting/TranslatableMessage.java b/src/main/java/net/knarcraft/permissionsigns/formatting/TranslatableMessage.java index 16a0134..707c18b 100644 --- a/src/main/java/net/knarcraft/permissionsigns/formatting/TranslatableMessage.java +++ b/src/main/java/net/knarcraft/permissionsigns/formatting/TranslatableMessage.java @@ -16,9 +16,44 @@ public enum TranslatableMessage { SIGN_PREFIX, /** - * The text to display as a permission sign's duration time unit + * The text to display for 1 second */ - SIGN_TIME_UNIT, + UNIT_SECOND, + + /** + * The text to display for a number of seconds + */ + UNIT_SECONDS, + + /** + * The text to display for 1 minute + */ + UNIT_MINUTE, + + /** + * The text to display for a number of minutes + */ + UNIT_MINUTES, + + /** + * The text to display for 1 hour + */ + UNIT_HOUR, + + /** + * The text to display for a number of hours + */ + UNIT_HOURS, + + /** + * The text to display for 1 day + */ + UNIT_DAY, + + /** + * The text to display for a number of days + */ + UNIT_DAYS, /** * The text to display on a permission sign that is entirely free @@ -50,6 +85,11 @@ public enum TranslatableMessage { */ PERMISSION_SIGN_DESTROY_DENY, + /** + * The message to display to a non-sneaking player trying to destroy a permissions sign + */ + PERMISSION_SIGN_DESTROY_SNEAK, + /** * The error message to display if the user has not provided necessary information to create a permission sign */ @@ -113,6 +153,11 @@ public enum TranslatableMessage { /** * The message to display when a reload action has been successfully performed */ - RELOAD_SUCCESSFUL + RELOAD_SUCCESSFUL, + + /** + * The text describing all properties of a permission sign + */ + SIGN_INFO } diff --git a/src/main/java/net/knarcraft/permissionsigns/formatting/Translator.java b/src/main/java/net/knarcraft/permissionsigns/formatting/Translator.java index 5565382..6da4a9d 100644 --- a/src/main/java/net/knarcraft/permissionsigns/formatting/Translator.java +++ b/src/main/java/net/knarcraft/permissionsigns/formatting/Translator.java @@ -6,7 +6,6 @@ import org.bukkit.configuration.file.YamlConfiguration; import java.io.BufferedReader; import java.io.FileNotFoundException; -import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; @@ -57,7 +56,7 @@ public final class Translator { BufferedReader reader; try { reader = FileHelper.getBufferedReaderForInternalFile("/strings.yml"); - } catch (FileNotFoundException | UnsupportedEncodingException e) { + } catch (FileNotFoundException e) { PermissionSigns.getInstance().getLogger().log(Level.SEVERE, "Unable to load translated messages"); return null; } diff --git a/src/main/java/net/knarcraft/permissionsigns/listener/BlockListener.java b/src/main/java/net/knarcraft/permissionsigns/listener/BlockListener.java index f598caa..e54a49c 100644 --- a/src/main/java/net/knarcraft/permissionsigns/listener/BlockListener.java +++ b/src/main/java/net/knarcraft/permissionsigns/listener/BlockListener.java @@ -6,12 +6,19 @@ import net.knarcraft.permissionsigns.manager.SignManager; import org.bukkit.Material; import org.bukkit.Tag; import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; import org.bukkit.block.Sign; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockBreakEvent; +import java.util.ArrayList; +import java.util.List; + +/** + * A listener for relevant block events such as signs being broken + */ public class BlockListener implements Listener { @EventHandler @@ -23,11 +30,60 @@ 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. + //} + Material material = block.getBlockData().getMaterial(); - if (!Tag.SIGNS.isTagged(material) && !Tag.WALL_SIGNS.isTagged(material)) { - return; + if (Tag.SIGNS.isTagged(material) || Tag.WALL_SIGNS.isTagged(material)) { + checkIfBlockIsPermissionSign(block, player, event); + if (event.isCancelled()) { + return; + } } + Block relativeBlock = block.getRelative(BlockFace.UP); + if (Tag.SIGNS.isTagged(relativeBlock.getBlockData().getMaterial())) { + checkIfBlockIsPermissionSign(relativeBlock, player, event); + if (event.isCancelled()) { + return; + } + } + + 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; + } + } + } + for (BlockFace blockFace : getRelevantBlockFaces()) { + relativeBlock = block.getRelative(blockFace); + if (Tag.WALL_SIGNS.isTagged(relativeBlock.getBlockData().getMaterial())) { + checkIfBlockIsPermissionSign(relativeBlock, player, event); + if (event.isCancelled()) { + return; + } + } + } + + //TODO: Need to protect against other things that might damage the sign, such as explosions + } + + /** + * Check if the given block is a permission sign, and perform necessary tasks if it is + * + * @param block

The block to check

+ * @param player

The player that caused the event

+ * @param event

The triggered block break event

+ */ + private void checkIfBlockIsPermissionSign(Block block, Player player, BlockBreakEvent event) { Sign sign = (Sign) block.getState(); boolean registered = SignManager.getSign(sign.getLocation()) != null; if (!registered) { @@ -37,12 +93,28 @@ public class BlockListener implements Listener { event.setCancelled(true); player.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.PERMISSION_SIGN_DESTROY_DENY)); } else { + if (!player.isSneaking()) { + event.setCancelled(true); + player.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.PERMISSION_SIGN_DESTROY_SNEAK)); + return; + } SignManager.removeSign(sign.getLocation()); player.sendMessage(StringFormatter.getTranslatedInfoMessage(TranslatableMessage.PERMISSION_SIGN_REMOVED)); } + } - //TODO: Need to protect against other things that might damage the sign, such as explosions - //TODO: Only allow the sign to be broken if shift-clicking? + /** + * Gets the relevant block faces for checking around a block + * + * @return

The relevant block faces for checking around a block

+ */ + private List getRelevantBlockFaces() { + List relevantBlockFaces = new ArrayList<>(); + relevantBlockFaces.add(BlockFace.WEST); + relevantBlockFaces.add(BlockFace.EAST); + relevantBlockFaces.add(BlockFace.NORTH); + relevantBlockFaces.add(BlockFace.SOUTH); + return relevantBlockFaces; } } diff --git a/src/main/java/net/knarcraft/permissionsigns/listener/SignListener.java b/src/main/java/net/knarcraft/permissionsigns/listener/SignListener.java index 9dbf814..5c998ba 100644 --- a/src/main/java/net/knarcraft/permissionsigns/listener/SignListener.java +++ b/src/main/java/net/knarcraft/permissionsigns/listener/SignListener.java @@ -9,6 +9,8 @@ import net.knarcraft.permissionsigns.formatting.Translator; import net.knarcraft.permissionsigns.manager.EconomyManager; import net.knarcraft.permissionsigns.manager.PermissionManager; import net.knarcraft.permissionsigns.manager.SignManager; +import net.md_5.bungee.api.ChatColor; +import org.bukkit.GameMode; import org.bukkit.Material; import org.bukkit.Tag; import org.bukkit.block.Block; @@ -16,9 +18,11 @@ import org.bukkit.block.Sign; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; import java.util.Arrays; +import java.util.List; import java.util.StringJoiner; /** @@ -46,7 +50,7 @@ public class SignListener implements Listener { } Sign sign = (Sign) block.getState(); - handleSignClick(sign, player); + handleSignClick(sign, player, event.getAction()); } /** @@ -54,19 +58,26 @@ public class SignListener implements Listener { * * @param sign

The clicked sign

* @param player

The player that clicked the sign

+ * @param action

The hand the player used to interact with the sign

*/ - private void handleSignClick(Sign sign, Player player) { + private void handleSignClick(Sign sign, Player player, Action action) { //Check if the sign is a registered permission sign PermissionSign permissionSign = SignManager.getSign(sign.getLocation()); if (permissionSign != null) { + if (player.getGameMode() == GameMode.CREATIVE && action == Action.LEFT_CLICK_BLOCK) { + return; + } if (!player.hasPermission("permissionsigns.use")) { player.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.INTERACT_PERMISSION_MISSING)); return; } - handlePermissionSignInteract(permissionSign, player); + if (player.isSneaking()) { + handlePermissionSignInteract(permissionSign, player); + } else { + player.sendMessage(getSignInfoText(permissionSign)); + } return; } - //TODO: Display information about granted permissions when shift-clicking? //Check if the player has a creation request that can be fulfilled SignCreationRequest request = PermissionSigns.getSignCreationRequest(player.getUniqueId()); @@ -75,6 +86,31 @@ public class SignListener implements Listener { } } + /** + * Gets information about a sign + * + * @param sign

The sign to get information about

+ * @return

The information to display

+ */ + private String getSignInfoText(PermissionSign sign) { + String rawSignInfo = Translator.getTranslatedMessage(TranslatableMessage.SIGN_INFO); + StringBuilder permissionString = new StringBuilder(); + List permissionNodes = sign.getPermissionNodes(); + if (permissionNodes.size() == 1) { + permissionString = new StringBuilder(permissionNodes.get(0)); + } else { + for (String permissionNode : permissionNodes) { + permissionString.append("\n" + "&f| &7- ").append(permissionNode); + } + permissionString.append("\n"); + } + + String replacedString = StringFormatter.replacePlaceholders(rawSignInfo, new String[]{"{Name}", "{Permissions}", + "{Cost}", "{Duration}"}, new String[]{sign.getName(), permissionString.toString(), sign.getCostString(), + sign.getDurationString()}); + return ChatColor.translateAlternateColorCodes('&', replacedString); + } + /** * Handles the interaction with a permission sign * @@ -137,9 +173,8 @@ public class SignListener implements Listener { String grantedPermissions = permissionsJoiner.toString(); if (permissionSign.getDuration() > 0) { String successMessage = StringFormatter.getTranslatedInfoMessage(TranslatableMessage.PERMISSIONS_GRANTED); - String timeUnit = Translator.getTranslatedMessage(TranslatableMessage.SIGN_TIME_UNIT); player.sendMessage(StringFormatter.replacePlaceholders(successMessage, new String[]{"{permissions}", "{time}"}, - new String[]{grantedPermissions, permissionSign.getDuration() + " " + timeUnit})); + new String[]{grantedPermissions, permissionSign.getDurationString()})); } else { player.sendMessage(StringFormatter.replacePlaceholder(StringFormatter.getTranslatedInfoMessage( TranslatableMessage.PERMISSIONS_GRANTED_PERMANENTLY), "{permissions}", grantedPermissions)); diff --git a/src/main/java/net/knarcraft/permissionsigns/utility/FileHelper.java b/src/main/java/net/knarcraft/permissionsigns/utility/FileHelper.java index d479374..d5a5c00 100644 --- a/src/main/java/net/knarcraft/permissionsigns/utility/FileHelper.java +++ b/src/main/java/net/knarcraft/permissionsigns/utility/FileHelper.java @@ -4,7 +4,6 @@ import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; /** @@ -18,7 +17,7 @@ public class FileHelper { * @return

A buffered read for reading the file

* @throws FileNotFoundException

If unable to get an input stream for the given file

*/ - public static BufferedReader getBufferedReaderForInternalFile(String file) throws FileNotFoundException, UnsupportedEncodingException { + public static BufferedReader getBufferedReaderForInternalFile(String file) throws FileNotFoundException { InputStream inputStream = FileHelper.class.getResourceAsStream(file); if (inputStream == null) { throw new FileNotFoundException("Unable to read the given file"); diff --git a/src/main/resources/strings.yml b/src/main/resources/strings.yml index 043931d..0ba01f7 100644 --- a/src/main/resources/strings.yml +++ b/src/main/resources/strings.yml @@ -2,13 +2,21 @@ en: PREFIX: "[PermissionSigns]" SIGN_PREFIX: "&4[PermSign]" MISSING_CREATION_INFO: "&7You must specify a sign name and a comma-separated list of permissions to create a permission sign" - SIGN_TIME_UNIT: "seconds" + UNIT_SECOND: "second" + UNIT_SECONDS: "seconds" + UNIT_MINUTE: "minute" + UNIT_MINUTES: "minutes" + UNIT_HOUR: "hour" + UNIT_HOURS: "hours" + UNIT_DAY: "day" + UNIT_DAYS: "days" SIGN_PERMANENT: "Permanent" SIGN_COST_FREE: "Free" COST_INVALID_NUMBER: "&7The given cost is not a valid number" DURATION_INVALID_NUMBER: "&7The given duration is not a valid number" COMMAND_PLAYER_ONLY: "&7This command is only available to players" PERMISSION_SIGN_DESTROY_DENY: "&7You do not have permissions to delete permissions signs" + PERMISSION_SIGN_DESTROY_SNEAK: "&7You must be sneaking to remove a permissions sign" PERMISSION_SIGN_REMOVED: "&7Permissions sign removed" COMMAND_PERMISSION_DENIED: "&7You do not have necessary permissions to perform this command" CREATION_REQUEST_CREATED: "&7Permission Sign request created. Right-click an empty sign within 60 seconds to create the new permission sign" @@ -21,17 +29,26 @@ en: INTERACT_PERMISSION_MISSING: "&7You do not have the necessary permissions required to use permission signs" CREATION_REQUEST_CANCELLED: "&7Your last permission sign creation request has been cancelled" RELOAD_SUCCESSFUL: "&7The plugin has been successfully reloaded" + SIGN_INFO: "&f---- &4Permission Sign&f ----\n&f| &bName: &7{Name}\n&f| &bPermission(s): &7{Permissions}\n&f| &bCost: &7{Cost}\n&f| &bDuration: &7{Duration}\n&f| &7Right-click the sign while sneaking to buy the permission(s)\n&f-------------------------" nb-no: PREFIX: "[Tilgangsrettighetsskilt]" SIGN_PREFIX: "&4[PermSign]" MISSING_CREATION_INFO: "&7Du må spesifisere et skiltnavn og en komma-separert liste av tilgangsrettigheter for å opprette et tilgangsrettighetsskilt" - SIGN_TIME_UNIT: "sekunder" + UNIT_SECOND: "sekund" + UNIT_SECONDS: "sekunder" + UNIT_MINUTE: "minutt" + UNIT_MINUTES: "minutter" + UNIT_HOUR: "time" + UNIT_HOURS: "timer" + UNIT_DAY: "dag" + UNIT_DAYS: "dager" SIGN_PERMANENT: "Permanent" SIGN_COST_FREE: "Gratis" COST_INVALID_NUMBER: "&7Den gitte kostnaden er ikke et gyldig nummer" DURATION_INVALID_NUMBER: "&7Den gitte varigheten er ikke et gyldig nummer" COMMAND_PLAYER_ONLY: "&7Denne kommandoen kan bare brukes av spillere" PERMISSION_SIGN_DESTROY_DENY: "&7Du har ikke tillatelse til å ødelegge tilgangsrettighetsskilter" + PERMISSION_SIGN_DESTROY_SNEAK: "&7Du må snike for å fjerne et tilgangsrettighetsskilt" PERMISSION_SIGN_REMOVED: "&7Tilgangsrettighetsskilt fjernet" COMMAND_PERMISSION_DENIED: "&7Du har ikke de nødvendige tilgangsrettighetene for å kjøre denne kommandoen" CREATION_REQUEST_CREATED: "&7Tilgangsrettighetsskiltsforespørsel opprettet. Høyre-klikk på et tomt skilt innen 60 sekunder for å opprette det nye tilgangsrettighetsskiltet" @@ -43,4 +60,5 @@ nb-no: PERMISSION_WORLD_INVALID: "&7Verdenen \"{world}\" er ikke et gyldig verdensnavn på denne serveren!" INTERACT_PERMISSION_MISSING: "&7Du har ikke de nødvendige tilgangsrettighetene for å bruke tilgangsrettighetsskilt" CREATION_REQUEST_CANCELLED: "&7Din siste tilgangsrettighetsskiltsforespørsel har blitt kansellert" - RELOAD_SUCCESSFUL: "&7Plugin-modulen har blitt lastet inn på nytt" \ No newline at end of file + RELOAD_SUCCESSFUL: "&7Plugin-modulen har blitt lastet inn på nytt" + SIGN_INFO: "&f---- &4Tilgangsrettighetsskilt&f ----\n&f| &bNavn: &7{Name}\n&f| &bTilgangsrettighet(er): &7{Permissions}\n&f| &bKostnad: &7{Cost}\n&f| &bVarighet: &7{Duration}\n&f| &7Høyreklikk skiltet mens du sniker for å kjøpe tilgangsrettighet(en/ene)\n&f-------------------------" \ No newline at end of file