Adds Vault integration

Adds the EconomyManager class for handling Vault payments
Adds the unfinished PermissionManager class for granting permissions through Vault
Adds the GPLv3 license
Fixes dependencies
Adds some code for withdrawing money and granting permissions when a sign is clicked
Changes cost to a double to be compliant with Vault
This commit is contained in:
Kristian Knarvik 2022-01-14 10:30:44 +01:00
parent 71e932bf40
commit 52b033c005
10 changed files with 318 additions and 46 deletions

44
pom.xml
View File

@ -10,8 +10,15 @@
<packaging>jar</packaging>
<name>PermissionSigns</name>
<description>A plugin for selling permissions using signs</description>
<licenses>
<license>
<name>GNU General Public License</name>
<url>https://www.gnu.org/licenses/gpl-3.0.en.html</url>
</license>
</licenses>
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@ -19,6 +26,7 @@
<url>git.knarcraft.net</url>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@ -29,29 +37,7 @@
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<repositories>
@ -63,6 +49,10 @@
<id>sonatype</id>
<url>https://oss.sonatype.org/content/groups/public/</url>
</repository>
<repository>
<id>vault-repo</id>
<url>http://nexus.hc.to/content/repositories/pub_releases</url>
</repository>
</repositories>
<dependencies>
@ -75,8 +65,14 @@
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>RELEASE</version>
<version>20.1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.milkbowl.vault</groupId>
<artifactId>VaultAPI</artifactId>
<version>1.7</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -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<Permission> permissionProvider = servicesManager.getRegistration(Permission.class);
RegisteredServiceProvider<Economy> 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();

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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<String> 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 <p>The duration, in seconds, until the permission should be revoked. 0 for non-temporary</p>
* @param cost <p>The cost of using this permission sign</p>
*/
public PermissionSign(Location signLocation, String name, List<String> permissionNodes, int duration, int cost) {
public PermissionSign(Location signLocation, String name, List<String> 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 <p>The duration, in seconds, until the permission should be revoked. 0 for non-temporary</p>
* @param cost <p>The cost of using this permission sign</p>
*/
public PermissionSign(String name, List<String> permissionNodes, int duration, int cost) {
public PermissionSign(String name, List<String> 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 <p>The cost of using this permission sign</p>
*/
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);
}
}

View File

@ -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 <p>The player the temporary permission was assigned to</p>
* @param permissionNode <p>The permission node granted to the player</p>
* @param duration <p>The duration, in seconds, the temporary permission should last</p>
*/
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 <p>The player the temporary permission was assigned to</p>
* @param permissionNode <p>The permission node granted to the player</p>
* @param grantedTime <p>The time this temporary permission was granted</p>
* @param duration <p>The duration, in seconds, the temporary permission should last</p>
*/
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 <p>The time (current time millis) this permission was granted</p>
*/
public long getGrantedTime() {
return this.grantedTime;
}
/**
* Gets the duration the player should be granted this permission for
*
* @return <p>The duration of the temporary permission</p>
*/
public int getGrantedDuration() {
return this.duration;
}
/**
* Gets the player this temporary permission was granted to
*
* @return <p>The player this temporary permission was granted to</p>
*/
public OfflinePlayer getGrantedPlayer() {
return grantedPlayer;
}
/**
* Gets the permission node that was granted to this player
*
* @return <p>The permission node that was granted to this player</p>
*/
public String getPermissionNode() {
return permissionNode;
}
}

View File

@ -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 <p>The player that clicked the sign</p>
*/
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 <p>The permission sign that was interacted with</p>
* @param player <p>The player that interacted with the permission sign</p>
*/
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 <p>The sign to register as a permission sign</p>
* @param request <p>The creation request to fulfill</p>
* @param player <p>The player that interacted with the sign</p>
*/
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
}
}

View File

@ -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 <p>The economy object to use for everything economy-related</p>
*/
public static void initialize(Economy economy) {
EconomyManager.economy = economy;
}
/**
* Checks whether the given player can afford the given cost
*
* @param player <p>The player to pay the cost</p>
* @param cost <p>The cost the player needs to pay</p>
* @return <p>True if the player is able to afford the cost</p>
*/
public static boolean canAfford(OfflinePlayer player, double cost) {
return economy.has(player, cost);
}
/**
* Gets the name of the used currency
*
* @param plural <p>Whether to get the plural name or the singular name</p>
* @return <p>The name of the used currency</p>
*/
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 <p>The player to withdraw money from</p>
* @param cost <p>The amount of money to withdraw</p>
*/
public static void withdraw(Player player, double cost) {
economy.withdrawPlayer(player, cost);
}
}

View File

@ -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<TemporaryPermission> temporaryPermissions;
/**
* Initializes the permission manager
*
* @param permission <p>The permission object to use for everything permission-related</p>
*/
public static void initialize(Permission permission) {
PermissionManager.permission = permission;
}
/**
* Grants a permission to the given player
*
* @param player <p>The player to grant the permission to</p>
* @param permissionNode <p>The permission node to grant the player</p>
* @param permanent <p>Whether to permanently grant the permission node</p>
*/
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 <p>The player to grant the permission to</p>
* @param permissionNode <p>The permission node to grant to the player</p>
*/
private static void addPermission(Player player, String permissionNode) {
permission.playerAdd(player, permissionNode);
}
/**
* Grants a temporary permission to a player
*
* @param player <p>The player to give the permission to</p>
* @param permissionNode <p>The temporary permission to grant</p>
*/
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 <p>The player to remove the permission from</p>
* @param permissionNode <p>The permission node to remove from the player</p>
*/
public static void removeTemporaryPermission(OfflinePlayer player, String permissionNode) {
permission.playerRemoveTransient(player, permissionNode);
}
}

View File

@ -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);