diff --git a/README.md b/README.md index 8dbf084..bc8ac35 100644 --- a/README.md +++ b/README.md @@ -51,9 +51,9 @@ An in-game description of available commands is available through the /bwb comma book from your personal directory * /givepublicbook \[# of copies (num)] \[signed (true/false)] - Same as givebook, but uses books from the public directory -* /loadbook \[# of copies] \[signed (true/false)] - Creates a book from the specified file and gives - it to the player. If no file is specified, a list of available files is returned. If true is specified, the book will - be signed, if false it will be unsigned +* /loadbook \[# of copies] \[signed (true/false)] - Creates a book from the specified file and + gives it to the player. If no file is specified, a list of available files is returned. If true is specified, the book + will be signed, if false it will be unsigned * /loadpublicbook \[# of copies] \[signed (true/false)] - Same as loadbook, but views files in the public directory * /reload - Reloads BwB's configuration file diff --git a/pom.xml b/pom.xml index 590cabd..2da778e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ net.knarcraft BooksWithoutBorders - 1.2.1 + 1.2.2 jar diff --git a/src/main/java/net/knarcraft/bookswithoutborders/BooksWithoutBorders.java b/src/main/java/net/knarcraft/bookswithoutborders/BooksWithoutBorders.java index 06b5fca..c2956f2 100644 --- a/src/main/java/net/knarcraft/bookswithoutborders/BooksWithoutBorders.java +++ b/src/main/java/net/knarcraft/bookswithoutborders/BooksWithoutBorders.java @@ -25,12 +25,14 @@ import net.knarcraft.bookswithoutborders.listener.BookEventListener; import net.knarcraft.bookswithoutborders.listener.PlayerEventListener; import net.knarcraft.bookswithoutborders.listener.SignEventListener; import net.knarcraft.bookswithoutborders.utility.FileHelper; +import net.knarcraft.bookswithoutborders.utility.UpdateChecker; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.PluginCommand; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.inventory.ItemFactory; +import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; @@ -106,6 +108,10 @@ public class BooksWithoutBorders extends JavaPlugin { config.options().copyDefaults(true); this.saveDefaultConfig(); + //Get plugin info + PluginDescriptionFile pluginDescriptionFile = this.getDescription(); + String pluginVersion = pluginDescriptionFile.getVersion(); + booksWithoutBorders = this; consoleSender = this.getServer().getConsoleSender(); playerBooksList = new HashMap<>(); @@ -123,6 +129,9 @@ public class BooksWithoutBorders extends JavaPlugin { } registerCommands(); + + UpdateChecker.checkForUpdate(this, "https://api.spigotmc.org/legacy/update.php?resource=96069", + () -> pluginVersion, null); } /** diff --git a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandCopy.java b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandCopy.java index 1227ecd..aa600f4 100644 --- a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandCopy.java +++ b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandCopy.java @@ -90,7 +90,7 @@ public class CommandCopy implements TabExecutor { public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { int argumentCount = args.length; if (argumentCount == 1) { - return TabCompletionHelper.getNumbers(1, 20); + return TabCompletionHelper.filterMatchingStartsWith(TabCompletionHelper.getNumbers(1, 20), args[0]); } return new ArrayList<>(); } diff --git a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandDelete.java b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandDelete.java index db8680e..9dd5c5e 100644 --- a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandDelete.java +++ b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandDelete.java @@ -3,6 +3,7 @@ package net.knarcraft.bookswithoutborders.command; import net.knarcraft.bookswithoutborders.BooksWithoutBorders; import net.knarcraft.bookswithoutborders.utility.FileHelper; import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper; +import net.knarcraft.bookswithoutborders.utility.TabCompletionHelper; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; @@ -123,7 +124,8 @@ public class CommandDelete implements TabExecutor { protected List doTabCompletion(CommandSender sender, String[] args, boolean deletePublic) { int argumentCount = args.length; if (argumentCount == 1) { - return BooksWithoutBorders.getAvailableBooks(sender, deletePublic); + return TabCompletionHelper.filterMatchingContains(BooksWithoutBorders.getAvailableBooks(sender, deletePublic), + args[0]); } return new ArrayList<>(); } diff --git a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandEncrypt.java b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandEncrypt.java index 9ee12f6..7d66aef 100644 --- a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandEncrypt.java +++ b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandEncrypt.java @@ -5,6 +5,7 @@ import net.knarcraft.bookswithoutborders.state.EncryptionStyle; import net.knarcraft.bookswithoutborders.state.ItemSlot; import net.knarcraft.bookswithoutborders.utility.EncryptionHelper; import net.knarcraft.bookswithoutborders.utility.InventoryHelper; +import net.knarcraft.bookswithoutborders.utility.TabCompletionHelper; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; @@ -125,12 +126,13 @@ public class CommandEncrypt implements TabExecutor { info.add(""); return info; } else { - return encryptionStyles; + return TabCompletionHelper.filterMatchingStartsWith(encryptionStyles, args[1]); } } else if (argumentsCount == 3 && groupEncrypt) { - return encryptionStyles; + return TabCompletionHelper.filterMatchingStartsWith(encryptionStyles, args[2]); } return new ArrayList<>(); } + } diff --git a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandGive.java b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandGive.java index f0923f6..e1ec2ae 100644 --- a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandGive.java +++ b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandGive.java @@ -132,18 +132,19 @@ public class CommandGive implements TabExecutor { if (argumentCount == 1) { //Return list of books - return BooksWithoutBorders.getAvailableBooks(sender, listPublic); + return TabCompletionHelper.filterMatchingContains(BooksWithoutBorders.getAvailableBooks(sender, listPublic), + args[0]); } else if (argumentCount == 2) { //Return online players return null; } else if (argumentCount == 3) { //Number of copies - return TabCompletionHelper.getBooleansAndNumbers(1, 3); + return TabCompletionHelper.filterMatchingStartsWith(TabCompletionHelper.getBooleansAndNumbers(1, 3), args[2]); } else if (argumentCount == 4) { //Signed try { Integer.parseInt(args[2]); - return TabCompletionHelper.getBooleans(); + return TabCompletionHelper.filterMatchingStartsWith(TabCompletionHelper.getBooleans(), args[3]); } catch (NumberFormatException e) { return new ArrayList<>(); } diff --git a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandLoad.java b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandLoad.java index 85f8466..1538c3c 100644 --- a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandLoad.java +++ b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandLoad.java @@ -110,15 +110,16 @@ public class CommandLoad implements TabExecutor { int argumentCount = args.length; if (argumentCount == 1) { //Return list of books - return BooksWithoutBorders.getAvailableBooks(sender, loadPublic); + return TabCompletionHelper.filterMatchingContains(BooksWithoutBorders.getAvailableBooks(sender, loadPublic), + args[0]); } else if (argumentCount == 2) { //Number of copies - return TabCompletionHelper.getBooleansAndNumbers(1, 3); + return TabCompletionHelper.filterMatchingStartsWith(TabCompletionHelper.getBooleansAndNumbers(1, 3), args[1]); } else if (argumentCount == 3) { //Signed try { Integer.parseInt(args[1]); - return TabCompletionHelper.getBooleans(); + return TabCompletionHelper.filterMatchingStartsWith(TabCompletionHelper.getBooleans(), args[2]); } catch (NumberFormatException e) { return new ArrayList<>(); } diff --git a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandSetBookPrice.java b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandSetBookPrice.java index bc8d7e4..2c8f681 100644 --- a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandSetBookPrice.java +++ b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandSetBookPrice.java @@ -22,6 +22,7 @@ import java.util.List; public class CommandSetBookPrice implements TabExecutor { private final BooksWithoutBorders booksWithoutBorders = BooksWithoutBorders.getInstance(); + private List paymentTypes; @Override public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { @@ -135,16 +136,26 @@ public class CommandSetBookPrice implements TabExecutor { @Override public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) { + if (paymentTypes == null) { + initializeTabCompleteLists(); + } + int argumentCount = args.length; if (argumentCount == 1) { - List paymentTypes = new ArrayList<>(); - paymentTypes.add("item"); - paymentTypes.add("eco"); - return paymentTypes; + return TabCompletionHelper.filterMatchingStartsWith(paymentTypes, args[0]); } else if (argumentCount == 2) { - return TabCompletionHelper.getNumbers(1, 3); + return TabCompletionHelper.filterMatchingStartsWith(TabCompletionHelper.getNumbers(1, 3), args[1]); } return new ArrayList<>(); } + /** + * Initializes the lists of tab complete values + */ + private void initializeTabCompleteLists() { + paymentTypes = new ArrayList<>(); + paymentTypes.add("item"); + paymentTypes.add("eco"); + } + } diff --git a/src/main/java/net/knarcraft/bookswithoutborders/utility/FileHelper.java b/src/main/java/net/knarcraft/bookswithoutborders/utility/FileHelper.java index b5afa47..059f1c9 100644 --- a/src/main/java/net/knarcraft/bookswithoutborders/utility/FileHelper.java +++ b/src/main/java/net/knarcraft/bookswithoutborders/utility/FileHelper.java @@ -4,7 +4,11 @@ import net.knarcraft.bookswithoutborders.BooksWithoutBorders; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; +import java.io.BufferedReader; import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -173,4 +177,15 @@ public final class FileHelper { return foundDuplicates; } + /** + * Gets a buffered reader given an input stream + * + * @param inputStream

The input stream to read

+ * @return

A buffered reader reading the input stream

+ */ + public static BufferedReader getBufferedReaderFromInputStream(InputStream inputStream) { + InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); + return new BufferedReader(inputStreamReader); + } + } diff --git a/src/main/java/net/knarcraft/bookswithoutborders/utility/TabCompletionHelper.java b/src/main/java/net/knarcraft/bookswithoutborders/utility/TabCompletionHelper.java index 41a814d..b40454c 100644 --- a/src/main/java/net/knarcraft/bookswithoutborders/utility/TabCompletionHelper.java +++ b/src/main/java/net/knarcraft/bookswithoutborders/utility/TabCompletionHelper.java @@ -11,6 +11,40 @@ public final class TabCompletionHelper { private TabCompletionHelper() { } + /** + * Finds tab complete values that contain the typed text + * + * @param values

The values to filter

+ * @param typedText

The text the player has started typing

+ * @return

The given string values that contain the player's typed text

+ */ + public static List filterMatchingContains(List values, String typedText) { + List configValues = new ArrayList<>(); + for (String value : values) { + if (value.toLowerCase().contains(typedText.toLowerCase())) { + configValues.add(value); + } + } + return configValues; + } + + /** + * Finds tab complete values that match the start of the typed text + * + * @param values

The values to filter

+ * @param typedText

The text the player has started typing

+ * @return

The given string values that start with the player's typed text

+ */ + public static List filterMatchingStartsWith(List values, String typedText) { + List configValues = new ArrayList<>(); + for (String value : values) { + if (value.toLowerCase().startsWith(typedText.toLowerCase())) { + configValues.add(value); + } + } + return configValues; + } + /** * Gets a list of booleans * diff --git a/src/main/java/net/knarcraft/bookswithoutborders/utility/UpdateChecker.java b/src/main/java/net/knarcraft/bookswithoutborders/utility/UpdateChecker.java new file mode 100644 index 0000000..9e455db --- /dev/null +++ b/src/main/java/net/knarcraft/bookswithoutborders/utility/UpdateChecker.java @@ -0,0 +1,92 @@ +package net.knarcraft.bookswithoutborders.utility; + +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitScheduler; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.logging.Level; + +/** + * The update checker is responsible for looking for new updates + */ +public final class UpdateChecker { + + private final static String updateNotice = "A new update is available: %s (You are still on %s)"; + + private UpdateChecker() { + + } + + /** + * Checks if there's a new update available, and alerts the user if necessary + */ + public static void checkForUpdate(Plugin plugin, String apiResourceURL, Supplier getVersionMethod, + Consumer setVersionMethod) { + BukkitScheduler scheduler = plugin.getServer().getScheduler(); + scheduler.runTaskAsynchronously(plugin, () -> UpdateChecker.queryAPI(plugin, apiResourceURL, getVersionMethod, + setVersionMethod)); + } + + /** + * Queries the spigot API to check for a newer version, and informs the user + */ + private static void queryAPI(Plugin plugin, String APIResourceURL, Supplier getVersionMethod, + Consumer setVersionMethod) { + try { + InputStream inputStream = new URL(APIResourceURL).openStream(); + BufferedReader reader = FileHelper.getBufferedReaderFromInputStream(inputStream); + //There should only be one line of output + String newVersion = reader.readLine(); + reader.close(); + + String oldVersion = getVersionMethod.get(); + //If there is a newer version, notify the user + if (isVersionHigher(oldVersion, newVersion)) { + plugin.getLogger().log(Level.INFO, getUpdateAvailableString(newVersion, oldVersion)); + if (setVersionMethod != null) { + setVersionMethod.accept(newVersion); + } + } + } catch (IOException e) { + plugin.getLogger().log(Level.WARNING, "Unable to get newest version."); + } + } + + /** + * Gets the string to display to a user to alert about a new update + * + * @param newVersion

The new available plugin version

+ * @param oldVersion

The old (current) plugin version

+ * @return

The string to display

+ */ + public static String getUpdateAvailableString(String newVersion, String oldVersion) { + return String.format(updateNotice, newVersion, oldVersion); + } + + /** + * Decides whether one version number is higher than another + * + * @param oldVersion

The old version to check

+ * @param newVersion

The new version to check

+ * @return

True if the new version is higher than the old one

+ */ + public static boolean isVersionHigher(String oldVersion, String newVersion) { + String[] oldVersionParts = oldVersion.split("\\."); + String[] newVersionParts = newVersion.split("\\."); + int versionLength = Math.max(oldVersionParts.length, newVersionParts.length); + for (int i = 0; i < versionLength; i++) { + int oldVersionNumber = oldVersionParts.length > i ? Integer.parseInt(oldVersionParts[i]) : 0; + int newVersionNumber = newVersionParts.length > i ? Integer.parseInt(newVersionParts[i]) : 0; + if (newVersionNumber != oldVersionNumber) { + return newVersionNumber > oldVersionNumber; + } + } + return false; + } + +}