diff --git a/pom.xml b/pom.xml index 62e094d..b4d6f30 100644 --- a/pom.xml +++ b/pom.xml @@ -10,8 +10,15 @@ jar PermissionSigns - A plugin for selling permissions using signs + + + + GNU General Public License + https://www.gnu.org/licenses/gpl-3.0.en.html + + + 17 UTF-8 @@ -19,6 +26,7 @@ git.knarcraft.net + src/main/java org.apache.maven.plugins @@ -29,29 +37,7 @@ ${java.version} - - org.apache.maven.plugins - maven-shade-plugin - 3.2.4 - - - package - - shade - - - false - - - - - - - src/main/resources - true - - @@ -63,6 +49,10 @@ sonatype https://oss.sonatype.org/content/groups/public/ + + vault-repo + http://nexus.hc.to/content/repositories/pub_releases + @@ -75,8 +65,14 @@ org.jetbrains annotations - RELEASE + 20.1.0 compile + + net.milkbowl.vault + VaultAPI + 1.7 + provided + diff --git a/src/main/java/net/knarcraft/permissionsigns/PermissionSigns.java b/src/main/java/net/knarcraft/permissionsigns/PermissionSigns.java index c76e2fc..b9e616c 100644 --- a/src/main/java/net/knarcraft/permissionsigns/PermissionSigns.java +++ b/src/main/java/net/knarcraft/permissionsigns/PermissionSigns.java @@ -5,11 +5,17 @@ import net.knarcraft.permissionsigns.command.PermissionSignsTabCompleter; import net.knarcraft.permissionsigns.container.PermissionSign; import net.knarcraft.permissionsigns.container.SignCreationRequest; import net.knarcraft.permissionsigns.formatting.Translator; +import net.knarcraft.permissionsigns.manager.EconomyManager; +import net.knarcraft.permissionsigns.manager.PermissionManager; import net.knarcraft.permissionsigns.thread.SignCreationRequestTimeoutThread; +import net.milkbowl.vault.economy.Economy; +import net.milkbowl.vault.permission.Permission; import org.bukkit.Bukkit; import org.bukkit.command.PluginCommand; import org.bukkit.entity.Player; import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.RegisteredServiceProvider; +import org.bukkit.plugin.ServicesManager; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitScheduler; @@ -126,12 +132,28 @@ public final class PermissionSigns extends JavaPlugin { //player.addAttachment(this, "essentials.fly", true, seconds * 20); //Vault probably has some API to add permissions + //TODO: Store all temporary permissions with the time they were assigned and the time they should be granted. + // Set the permissions as permanent and remove when the timer expires? Might cause problems for players that has + // the permission inherited, unless we make sure the player does not have the permission before + // Alternatively: Manage all permissions ourselves, for temporary permissions, and add permission attachments + // on startup I guess. //TODO: Start sign creation when the create command is used and save the data until an empty sign is right-clicked //TODO: Check for existence of old permission signs when clicked and register them as new permission signs. If // it has the permissionSigns header and a name matching contents in signs.yml, add it. + //Check if vault is loaded + ServicesManager servicesManager = this.getServer().getServicesManager(); + RegisteredServiceProvider permissionProvider = servicesManager.getRegistration(Permission.class); + RegisteredServiceProvider economyProvider = servicesManager.getRegistration(Economy.class); + if (permissionProvider != null && economyProvider != null) { + EconomyManager.initialize(economyProvider.getProvider()); + PermissionManager.initialize(permissionProvider.getProvider()); + } else { + throw new IllegalStateException("[PermissionSigns] Error: Vault could not be loaded"); + } + Translator.loadLanguages("en"); registerCommands(); BukkitScheduler scheduler = Bukkit.getScheduler(); diff --git a/src/main/java/net/knarcraft/permissionsigns/command/CreateCommand.java b/src/main/java/net/knarcraft/permissionsigns/command/CreateCommand.java index eb93802..0d5f38b 100644 --- a/src/main/java/net/knarcraft/permissionsigns/command/CreateCommand.java +++ b/src/main/java/net/knarcraft/permissionsigns/command/CreateCommand.java @@ -55,10 +55,10 @@ public class CreateCommand implements CommandExecutor { private PermissionSign parseSign(@NotNull CommandSender sender, @NotNull String[] args) { String name = args[0]; String[] permissions = args[1].split(","); - int cost; + double cost; int duration; try { - cost = Integer.parseInt(args[2]); + cost = Double.parseDouble(args[2]); } catch (NumberFormatException exception) { sender.sendMessage(Translator.getTranslatedMessage(TranslatableMessage.COST_INVALID_NUMBER)); return null; @@ -70,7 +70,7 @@ public class CreateCommand implements CommandExecutor { return null; } - return new PermissionSign(name, List.of(permissions), cost, duration); + return new PermissionSign(name, List.of(permissions), duration, cost); } } diff --git a/src/main/java/net/knarcraft/permissionsigns/command/ReloadCommand.java b/src/main/java/net/knarcraft/permissionsigns/command/ReloadCommand.java index f1428b0..1dcfa7d 100644 --- a/src/main/java/net/knarcraft/permissionsigns/command/ReloadCommand.java +++ b/src/main/java/net/knarcraft/permissionsigns/command/ReloadCommand.java @@ -1,9 +1,9 @@ package net.knarcraft.permissionsigns.command; -import net.knarcraft.permissionsigns.SignManager; import net.knarcraft.permissionsigns.formatting.StringFormatter; import net.knarcraft.permissionsigns.formatting.TranslatableMessage; import net.knarcraft.permissionsigns.formatting.Translator; +import net.knarcraft.permissionsigns.manager.SignManager; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; diff --git a/src/main/java/net/knarcraft/permissionsigns/container/PermissionSign.java b/src/main/java/net/knarcraft/permissionsigns/container/PermissionSign.java index 2c39a6d..d16943f 100644 --- a/src/main/java/net/knarcraft/permissionsigns/container/PermissionSign.java +++ b/src/main/java/net/knarcraft/permissionsigns/container/PermissionSign.java @@ -2,6 +2,7 @@ package net.knarcraft.permissionsigns.container; import net.knarcraft.permissionsigns.formatting.TranslatableMessage; import net.knarcraft.permissionsigns.formatting.Translator; +import net.knarcraft.permissionsigns.manager.EconomyManager; import org.bukkit.ChatColor; import org.bukkit.Location; @@ -17,7 +18,7 @@ public class PermissionSign { private final String name; private final List permissionNodes; private final int duration; - private final int cost; + private final double cost; /** * Instantiates a new permission sign @@ -28,7 +29,7 @@ public class PermissionSign { * @param duration

The duration, in seconds, until the permission should be revoked. 0 for non-temporary

* @param cost

The cost of using this permission sign

*/ - public PermissionSign(Location signLocation, String name, List permissionNodes, int duration, int cost) { + public PermissionSign(Location signLocation, String name, List permissionNodes, int duration, double cost) { this.signLocation = signLocation; this.name = name; this.permissionNodes = new ArrayList<>(permissionNodes); @@ -46,7 +47,7 @@ public class PermissionSign { * @param duration

The duration, in seconds, until the permission should be revoked. 0 for non-temporary

* @param cost

The cost of using this permission sign

*/ - public PermissionSign(String name, List permissionNodes, int duration, int cost) { + public PermissionSign(String name, List permissionNodes, int duration, double cost) { this.name = name; this.permissionNodes = new ArrayList<>(permissionNodes); this.duration = Math.max(0, duration); @@ -111,7 +112,7 @@ public class PermissionSign { * * @return

The cost of using this permission sign

*/ - public int getCost() { + public double getCost() { return this.cost; } @@ -163,8 +164,8 @@ public class PermissionSign { if (cost == 0) { return Translator.getTranslatedMessage(TranslatableMessage.SIGN_COST_FREE); } else { - //TODO: Get currency unit from Vault - return cost + "$"; + String currency = EconomyManager.getCurrency(cost != 1); + return String.format("%f%s", cost, currency); } } diff --git a/src/main/java/net/knarcraft/permissionsigns/container/TemporaryPermission.java b/src/main/java/net/knarcraft/permissionsigns/container/TemporaryPermission.java new file mode 100644 index 0000000..f8a8c7f --- /dev/null +++ b/src/main/java/net/knarcraft/permissionsigns/container/TemporaryPermission.java @@ -0,0 +1,82 @@ +package net.knarcraft.permissionsigns.container; + +import org.bukkit.OfflinePlayer; + +/** + * A temporary permission that needs to be removed after a given duration + */ +public class TemporaryPermission { + + private final long grantedTime; + private final OfflinePlayer grantedPlayer; + private final String permissionNode; + private final int duration; + + /** + * Instantiates a new temporary permission + * + * @param player

The player the temporary permission was assigned to

+ * @param permissionNode

The permission node granted to the player

+ * @param duration

The duration, in seconds, the temporary permission should last

+ */ + public TemporaryPermission(OfflinePlayer player, String permissionNode, int duration) { + grantedTime = System.currentTimeMillis(); + this.grantedPlayer = player; + this.permissionNode = permissionNode; + this.duration = duration; + //TODO: Need to account for world. Perhaps have a config option to choose between setting permissions for all + // worlds, or just the world the sign and player is in + } + + /** + * Instantiates a new temporary permission + * + * @param player

The player the temporary permission was assigned to

+ * @param permissionNode

The permission node granted to the player

+ * @param grantedTime

The time this temporary permission was granted

+ * @param duration

The duration, in seconds, the temporary permission should last

+ */ + public TemporaryPermission(OfflinePlayer player, String permissionNode, long grantedTime, int duration) { + this.grantedPlayer = player; + this.permissionNode = permissionNode; + this.grantedTime = grantedTime; + this.duration = duration; + } + + /** + * Gets the time that this temporary permission was granted + * + * @return

The time (current time millis) this permission was granted

+ */ + public long getGrantedTime() { + return this.grantedTime; + } + + /** + * Gets the duration the player should be granted this permission for + * + * @return

The duration of the temporary permission

+ */ + public int getGrantedDuration() { + return this.duration; + } + + /** + * Gets the player this temporary permission was granted to + * + * @return

The player this temporary permission was granted to

+ */ + public OfflinePlayer getGrantedPlayer() { + return grantedPlayer; + } + + /** + * Gets the permission node that was granted to this player + * + * @return

The permission node that was granted to this player

+ */ + public String getPermissionNode() { + return permissionNode; + } + +} diff --git a/src/main/java/net/knarcraft/permissionsigns/listener/SignListener.java b/src/main/java/net/knarcraft/permissionsigns/listener/SignListener.java index dea9a8c..9722b97 100644 --- a/src/main/java/net/knarcraft/permissionsigns/listener/SignListener.java +++ b/src/main/java/net/knarcraft/permissionsigns/listener/SignListener.java @@ -1,11 +1,13 @@ package net.knarcraft.permissionsigns.listener; import net.knarcraft.permissionsigns.PermissionSigns; -import net.knarcraft.permissionsigns.SignManager; import net.knarcraft.permissionsigns.container.PermissionSign; import net.knarcraft.permissionsigns.container.SignCreationRequest; import net.knarcraft.permissionsigns.formatting.TranslatableMessage; 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 org.bukkit.Material; import org.bukkit.Tag; import org.bukkit.block.Block; @@ -78,23 +80,53 @@ public class SignListener implements Listener { * @param player

The player that clicked the sign

*/ private void handleSignRightClick(Sign sign, Player player) { - String[] lines = sign.getLines(); - - if (SignManager.getSign(sign.getLocation()) != null) { - //TODO: Perform the permission payment/granting + //Check if the sign is a registered permission sign + PermissionSign permissionSign = SignManager.getSign(sign.getLocation()); + if (permissionSign != null) { + handlePermissionSignInteract(permissionSign, player); return; } + //Check if the player has a creation request that can be fulfilled + SignCreationRequest request = PermissionSigns.getSignCreationRequest(player.getUniqueId()); + if (request != null) { + registerPermissionSign(sign, request, player); + } + } + + /** + * Handles the interaction with a permission sign + * + * @param permissionSign

The permission sign that was interacted with

+ * @param player

The player that interacted with the permission sign

+ */ + private void handlePermissionSignInteract(PermissionSign permissionSign, Player player) { + //TODO: Check if the player has the permissions for sale. If it has, just explain that it does + if (EconomyManager.canAfford(player, permissionSign.getCost())) { + EconomyManager.withdraw(player, permissionSign.getCost()); + for (String permissionString : permissionSign.getPermissionNodes()) { + PermissionManager.addPermission(player, permissionString, permissionSign.getDuration() == 0); + } + //TODO: Tell the player that they've been granted the new permissions, possibly including the full nodes? + } else { + //TODO: Tell the player that they cannot afford the transaction + } + } + + /** + * Registers a new permission sign if the given sign is empty + * + * @param sign

The sign to register as a permission sign

+ * @param request

The creation request to fulfill

+ * @param player

The player that interacted with the sign

+ */ + private void registerPermissionSign(Sign sign, SignCreationRequest request, Player player) { + String[] lines = sign.getLines(); //Don't allow non-empty signs to be overwritten if (!Arrays.stream(lines).allMatch(String::isEmpty)) { return; } - SignCreationRequest request = PermissionSigns.getSignCreationRequest(player.getUniqueId()); - if (request == null) { - return; - } - //Register the sign and remove the request PermissionSign permissionSign = request.getPermissionSign(); permissionSign.setSignLocation(sign.getLocation()); @@ -107,6 +139,7 @@ public class SignListener implements Listener { sign.setLine(i, signLines[i]); } sign.update(); + //TODO: Display text in the chat explaining that the permission sign has been created } } diff --git a/src/main/java/net/knarcraft/permissionsigns/manager/EconomyManager.java b/src/main/java/net/knarcraft/permissionsigns/manager/EconomyManager.java new file mode 100644 index 0000000..e5d690b --- /dev/null +++ b/src/main/java/net/knarcraft/permissionsigns/manager/EconomyManager.java @@ -0,0 +1,58 @@ +package net.knarcraft.permissionsigns.manager; + +import net.milkbowl.vault.economy.Economy; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +/** + * A manager that performs all Economy tasks + */ +public class EconomyManager { + + private static Economy economy; + + /** + * Initializes the economy manager + * + * @param economy

The economy object to use for everything economy-related

+ */ + public static void initialize(Economy economy) { + EconomyManager.economy = economy; + } + + /** + * Checks whether the given player can afford the given cost + * + * @param player

The player to pay the cost

+ * @param cost

The cost the player needs to pay

+ * @return

True if the player is able to afford the cost

+ */ + public static boolean canAfford(OfflinePlayer player, double cost) { + return economy.has(player, cost); + } + + /** + * Gets the name of the used currency + * + * @param plural

Whether to get the plural name or the singular name

+ * @return

The name of the used currency

+ */ + public static String getCurrency(boolean plural) { + if (plural) { + return economy.currencyNamePlural(); + } else { + return economy.currencyNameSingular(); + } + } + + /** + * Withdraws the given cost from the given player's account + * + * @param player

The player to withdraw money from

+ * @param cost

The amount of money to withdraw

+ */ + public static void withdraw(Player player, double cost) { + economy.withdrawPlayer(player, cost); + } + +} diff --git a/src/main/java/net/knarcraft/permissionsigns/manager/PermissionManager.java b/src/main/java/net/knarcraft/permissionsigns/manager/PermissionManager.java new file mode 100644 index 0000000..7c1be1d --- /dev/null +++ b/src/main/java/net/knarcraft/permissionsigns/manager/PermissionManager.java @@ -0,0 +1,79 @@ +package net.knarcraft.permissionsigns.manager; + +import net.knarcraft.permissionsigns.container.TemporaryPermission; +import net.milkbowl.vault.permission.Permission; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +import java.util.List; + +/** + * A manager that performs all Permission tasks + */ +public class PermissionManager { + + private static Permission permission; + private static List temporaryPermissions; + + /** + * Initializes the permission manager + * + * @param permission

The permission object to use for everything permission-related

+ */ + public static void initialize(Permission permission) { + PermissionManager.permission = permission; + } + + /** + * Grants a permission to the given player + * + * @param player

The player to grant the permission to

+ * @param permissionNode

The permission node to grant the player

+ * @param permanent

Whether to permanently grant the permission node

+ */ + public static void addPermission(Player player, String permissionNode, boolean permanent) { + if (permanent) { + addPermission(player, permissionNode); + } else { + addTemporaryPermission(player, permissionNode); + } + } + + /** + * Grants a permanent permission to a player + * + * @param player

The player to grant the permission to

+ * @param permissionNode

The permission node to grant to the player

+ */ + private static void addPermission(Player player, String permissionNode) { + permission.playerAdd(player, permissionNode); + } + + /** + * Grants a temporary permission to a player + * + * @param player

The player to give the permission to

+ * @param permissionNode

The temporary permission to grant

+ */ + private static void addTemporaryPermission(OfflinePlayer player, String permissionNode) { + permission.playerAddTransient(player, permissionNode); + //TODO: Create and store a temporary permission + // Check all stored temporary permissions on startup: + // * Remove expired temporary permissions + // * Grant transient permissions + // In a separate thread, remove any expired permissions, checking at least once every seconds. Might want to + // store the granted permissions in a priority queue where the priority is the least duration left. + // How to store temporary permissions? Identifier as Player + permission granted time? + } + + /** + * Removes a temporary permission + * + * @param player

The player to remove the permission from

+ * @param permissionNode

The permission node to remove from the player

+ */ + public static void removeTemporaryPermission(OfflinePlayer player, String permissionNode) { + permission.playerRemoveTransient(player, permissionNode); + } + +} diff --git a/src/main/java/net/knarcraft/permissionsigns/SignManager.java b/src/main/java/net/knarcraft/permissionsigns/manager/SignManager.java similarity index 97% rename from src/main/java/net/knarcraft/permissionsigns/SignManager.java rename to src/main/java/net/knarcraft/permissionsigns/manager/SignManager.java index 8875b1b..7d21a4c 100644 --- a/src/main/java/net/knarcraft/permissionsigns/SignManager.java +++ b/src/main/java/net/knarcraft/permissionsigns/manager/SignManager.java @@ -1,5 +1,6 @@ -package net.knarcraft.permissionsigns; +package net.knarcraft.permissionsigns.manager; +import net.knarcraft.permissionsigns.PermissionSigns; import net.knarcraft.permissionsigns.container.PermissionSign; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -142,7 +143,7 @@ public class SignManager { }); int duration = signSection.getInt(key + ".duration"); - int cost = signSection.getInt(key + ".cost"); + double cost = signSection.getDouble(key + ".cost"); PermissionSign loadedSign = new PermissionSign(signLocation, signName, permissionStrings, duration, cost); managedSigns.put(signLocation, loadedSign);