diff --git a/src/main/java/net/knarcraft/bookswithoutborders/BooksWithoutBorders.java b/src/main/java/net/knarcraft/bookswithoutborders/BooksWithoutBorders.java index 98124e0..bcb7359 100644 --- a/src/main/java/net/knarcraft/bookswithoutborders/BooksWithoutBorders.java +++ b/src/main/java/net/knarcraft/bookswithoutborders/BooksWithoutBorders.java @@ -18,12 +18,12 @@ import net.knarcraft.bookswithoutborders.state.EncryptionStyle; import net.knarcraft.bookswithoutborders.utility.BookFormatter; import net.knarcraft.bookswithoutborders.utility.EncryptionHelper; import net.knarcraft.bookswithoutborders.utility.FileHelper; +import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper; import net.knarcraft.bookswithoutborders.utility.InventoryHelper; import net.milkbowl.vault.economy.Economy; import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.OfflinePlayer; -import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; @@ -40,6 +40,9 @@ import org.bukkit.plugin.Plugin; import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.java.JavaPlugin; +import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.cleanString; +import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.fixName; + public class BooksWithoutBorders extends JavaPlugin { protected static int bookDuplicateLimit; //The separating string between the book title and the book author @@ -62,7 +65,7 @@ public class BooksWithoutBorders extends JavaPlugin { protected static ConsoleCommandSender consoleSender; public static String bookFolder; public static final ChatColor errorColor = ChatColor.RED; - protected static final ChatColor successColor = ChatColor.GREEN; + public static final ChatColor successColor = ChatColor.GREEN; public static final ChatColor commandColor = ChatColor.YELLOW; @Override @@ -86,7 +89,7 @@ public class BooksWithoutBorders extends JavaPlugin { registerCommand("delete", new CommandDelete(this), null); registerCommand("deletePublic", new CommandDeletePublic(this), null); registerCommand("copy", new CommandCopy(this), null); - registerCommand("unSign", new CommandUnSign(this), null); + registerCommand("unSign", new CommandUnSign(), null); registerCommand("encrypt", new CommandEncrypt(this), null); registerCommand("setBookPrice", new CommandSetBookPrice(this), null); registerCommand("setLore", new CommandSetLore(), null); @@ -96,12 +99,16 @@ public class BooksWithoutBorders extends JavaPlugin { registerCommand("setTitle", new CommandSetTitle(), null); registerCommand("load", new CommandLoad(this), null); registerCommand("loadPublic", new CommandLoadPublic(this), null); + registerCommand("booksWithoutBorders", new CommandBooksWithoutBorders(this), null); + registerCommand("reload", new CommandReload(this), null); + registerCommand("givePublic", new CommandGivePublic(this), null); } /** * Registers a command + * * @param commandName

The name of the command to register

- * @param executor

The executor to register for the command

+ * @param executor

The executor to register for the command

*/ private void registerCommand(String commandName, CommandExecutor executor, TabCompleter tabCompleter) { PluginCommand pluginCommand = this.getCommand(commandName); @@ -196,7 +203,7 @@ public class BooksWithoutBorders extends JavaPlugin { return loadExistingPlayers(); } - protected boolean loadConfig() { + public boolean loadConfig() { this.reloadConfig(); try { useYml = this.getConfig().getBoolean("Options.Save_Books_in_Yaml_Format", true); @@ -261,7 +268,7 @@ public class BooksWithoutBorders extends JavaPlugin { return (eco != null); } - protected boolean loadExistingPlayers() { + public boolean loadExistingPlayers() { File fTest = new File(this.getDataFolder().getAbsolutePath() + SLASH + "Existing Players.txt"); existingPlayers = new ArrayList<>(); @@ -325,236 +332,6 @@ public class BooksWithoutBorders extends JavaPlugin { return true; } - @Override - public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - - if ((command.getName().equalsIgnoreCase("BooksWithoutBorders") || command.getName().equalsIgnoreCase("bwb"))) { - if (args.length == 0 && sender instanceof Player) { - //Lists all commands - sender.sendMessage(commandColor + "Use: /bwb [Command]"); - sender.sendMessage(commandColor + "[] denote parameters"); - - if (booksHavePrice()) { - if (bookPriceType != Material.AIR) { - sendErrorMessage(sender, "[" + (int) bookPriceQuantity + " " + bookPriceType.toString() + "(s) are required to create a book]"); - } else { - sendErrorMessage(sender, "[" + BooksWithoutBorders.eco.format(bookPriceQuantity) + " is required to create a book]"); - } - } - sender.sendMessage(commandColor + "Commands:"); - - if (sender.hasPermission("bookswithoutborders.load")) { - sender.sendMessage(commandColor + "\nLoad [file name or number] [# of copies] [true/false]:"); - sendSuccessMessage(sender, "Creates a book from the specified file and gives it to the player"); - sendSuccessMessage(sender, "If no file is specified, a list of available files is returned"); - sendSuccessMessage(sender, "If true is specified the book will be signed, if false it will be"); - sendSuccessMessage(sender, "unsigned"); - } - if (sender.hasPermission("bookswithoutborders.loadPublic")) { - sender.sendMessage(commandColor + "loadPublic [file name or number] [# of copies] [true/false]:"); - sendSuccessMessage(sender, "Same as Load, but views files in the public directory"); - } - if (sender.hasPermission("bookswithoutborders.save")) { - sender.sendMessage("\n" + commandColor + "Save [true/false]: " + successColor + "Saves the book the player is"); - sendSuccessMessage(sender, "holding to a text file in a private directory"); - sendSuccessMessage(sender, "If true is specified, a book of the same name by the same"); - sendSuccessMessage(sender, "author will be overwritten by the new book"); - } - if (sender.hasPermission("bookswithoutborders.savePublic")) { - sender.sendMessage(commandColor + "savePublic [true/false]: " + successColor + "Same as Save,"); - sendSuccessMessage(sender, "but saves files in the public directory"); - } - if (sender.hasPermission("bookswithoutborders.give")) { - sender.sendMessage("\n" + commandColor + "Give [file name or number] [player name] [# of copies] [true/false]:"); - sendSuccessMessage(sender, "Gives the selected player a book from your personal directory"); - } - if (sender.hasPermission("bookswithoutborders.givePublic")) { - sender.sendMessage(commandColor + "givePublic [file name or number] [player name] [# of copies] [true/false]:"); - sendSuccessMessage(sender, "Same as give, but uses books from the public directory"); - } - if (sender.hasPermission("bookswithoutborders.delete")) { - sender.sendMessage(commandColor + "\nDelete [file name or number]: " + successColor + "Deletes the specified"); - sendSuccessMessage(sender, "file in the player's directory"); - sendSuccessMessage(sender, "If no file is specified, a list of available files is returned"); - } - if (sender.hasPermission("bookswithoutborders.admin")) { - sender.sendMessage(commandColor + "deletePublic [file name or number]: " + successColor + "Same as Delete,"); - sendSuccessMessage(sender, "but deletes files in the public directory"); - sender.sendMessage(commandColor + "\nReload:" + successColor + " Reloads BwB's configuration file"); - } - - if (sender.hasPermission("bookswithoutborders.unsign")) { - sender.sendMessage("\n" + commandColor + "Unsign: " + successColor + "Un-signs the book the player is holding"); - } - if (sender.hasPermission("bookswithoutborders.copy")) { - sender.sendMessage("\n" + commandColor + "Copy [number of copies]: " + successColor + "Copies the book the player is holding"); - } - - if (sender.hasPermission("bookswithoutborders.encrypt")) { - sender.sendMessage("\n" + commandColor + "Encrypt [key] [style]: " + successColor + "Encrypts the book the player is holding"); - sender.sendMessage(commandColor + "[key]" + successColor + " is required and can be any phrase or number excluding spaces"); - sender.sendMessage(commandColor + "[style]" + successColor + " is not required. Possible values are \"DNA\" or \"Magic\""); - } - if (sender.hasPermission("bookswithoutborders.groupEncrypt")) { - sender.sendMessage("\n" + commandColor + "groupEncrypt [group name] [key] [style]: " + successColor + "Encrypts book so that only players with the" + - "\n bookswithoutborders.decrypt." + commandColor + "[group name]" + successColor + " permission may decrypt the book by holding and left clicking the book"); - } - if (sender.hasPermission("bookswithoutborders.decrypt")) { - sender.sendMessage("\n" + commandColor + "Decrypt [key]: " + successColor + "Decrypts the book the player is holding"); - sender.sendMessage(commandColor + "[key]" + successColor + " is required and MUST be IDENTICAL to the key used to encrypt held book"); - } - if (sender.hasPermission("bookswithoutborders.setTitle")) { - sender.sendMessage("\n" + commandColor + "setTitle [title]: " + successColor + "Sets the title of the book/item the player is holding"); - } - if (sender.hasPermission("bookswithoutborders.setAuthor")) { - sender.sendMessage("\n" + commandColor + "setAuthor [author]: " + successColor + "Sets the author of the book the player is holding"); - } - if (sender.hasPermission("bookswithoutborders.setLore")) { - sender.sendMessage("\n" + commandColor + "setLore [lore]: " + successColor + "Sets the lore of the item the player is holding"); - sendSuccessMessage(sender, "Insert the lore_line_separator character to force a new line\n[\"~\" by default]"); - } - if (sender.hasPermission("bookswithoutborders.setBookPrice")) { - sender.sendMessage("\n" + commandColor + "setBookPrice [Item/Eco] [quantity]: " + successColor + "Sets the per-book-price to create a book via commands." + - "\nIf [Item], the item in the player's hand in the amount of [quantity] will be the price.\nIf [Eco], a Vault based economy will be used for price." + - "\nIf neither [Item/Eco] or [quantity] are specified the current price to create books will be removed."); - } - return true; - } - - if (args.length == 0) { - //lists commands from console - sender.sendMessage(commandColor + "Use: /bwb [Command]"); - sender.sendMessage(commandColor + "[] denote parameters"); - sender.sendMessage(commandColor + "Commands:"); - sender.sendMessage(commandColor + "\nReload:" + successColor + " Reloads BwB's config file"); - sender.sendMessage(commandColor + "givePublic [file name or number] [player name] [true/false]: " + successColor); - sendSuccessMessage(sender, "Gives the selected player a book from the public directory"); - sendSuccessMessage(sender, "If no file is specified, a list of available files is returned"); - sender.sendMessage(commandColor + "deletePublic [file name or number]: " + successColor + "Deletes the specified"); - sendSuccessMessage(sender, "file in the public directory"); - sendSuccessMessage(sender, "If no file is specified, a list of available files is returned"); - - return true; - } - - if (args[0].equalsIgnoreCase("reload")) { - if (sender instanceof Player) { - if (!sender.hasPermission("bookswithoutborders.admin") && !sender.hasPermission("*")) { - sendErrorMessage(sender, "You don't have permission to use this command!"); - return false; - } - } - - if (loadConfig() && loadExistingPlayers()) { - sendSuccessMessage(sender, "BooksWithoutBorders configuration reloaded!"); - } else { - sendErrorMessage(sender, "Reload Failed!"); - sendErrorMessage(sender, "See console for details"); - } - return true; - } - - if (args[0].equalsIgnoreCase("givePublic")) { - if (sender instanceof Player) { - if (!sender.hasPermission("bookswithoutborders.givePublic")) { - sendErrorMessage(sender, " You don't have permission to use this command!"); - return false; - } - } - - if (args.length == 2 || args.length > 5) { - sendErrorMessage(sender, "Incorrect number of arguments for this command!"); - return false; - } - - if (args.length == 1) { - loadList.put(sender.getName(), listFiles(sender, true, false)); - return true; - } - - for (int x = 0; x < args[1].length(); x++) { - if (!Character.isDigit(args[1].charAt(x))) - break; - if (x == args[1].length() - 1) - loadList.put(sender.getName(), listFiles(sender, true, true)); - } - - ItemStack newBook; - Player receivingPlayer = this.getServer().getPlayer(args[2]); - if (receivingPlayer == null) { - sendErrorMessage(sender, "Player not found!"); - return false; - } - - if (receivingPlayer.getInventory().firstEmpty() == -1) { - sendErrorMessage(sender, "Receiving player must have space in their inventory to receive books!"); - return false; - } - //bwb give [book name] [player] [numCopies] [is signed] - try { - if (args.length == 5) - newBook = loadBook(sender, cleanString(args[1]), args[4], "public", Integer.parseInt(args[3])); - else if (args.length == 4) { - if (args[3].equalsIgnoreCase("true") || args[3].equalsIgnoreCase("false")) - newBook = loadBook(sender, cleanString(args[1]), args[3], "public"); - else - newBook = loadBook(sender, cleanString(args[1]), "true", "public", Integer.parseInt(args[3])); - } else - newBook = loadBook(sender, cleanString(args[1]), "true", "public"); - - if (newBook != null) { - receivingPlayer.getInventory().addItem(newBook); - sendSuccessMessage(sender, "Book sent!"); - sendSuccessMessage(receivingPlayer, "Book received!"); - return true; - } else { - sendErrorMessage(sender, "Book failed to load!"); - return false; - } - } catch (NumberFormatException e) { - sendErrorMessage(sender, "Invalid number of book copies specified!"); - return false; - } - } - - sendErrorMessage(sender, "Command not recognized! Use " + commandColor + "/BwB" + errorColor + " for more info!"); - } - return false; - } - - /** - * Removes any special character from a filename - * @param fileName

The file name to clean

- * @return

The cleaned file name

- */ - public String cleanString(String fileName) { - fileName = fileName.replace("/", ""); - fileName = fileName.replace("\\", ""); - fileName = fileName.replace("*", ""); - fileName = fileName.replace(":", ""); - fileName = fileName.replace("|", ""); - fileName = fileName.replace("<", ""); - fileName = fileName.replace(">", ""); - fileName = fileName.replace("?", ""); - fileName = fileName.replace("\"", ""); - return fileName; - } - - /** - * Changes spaces to underscores or underscores to spaces, depending on context - * @param fileName

The file name to fix

- * @param isLoading

Whether loading from a file as opposed to saving to a file

- * @return

The fixed name

- */ - protected String fixName(String fileName, Boolean isLoading) { - if (isLoading) { - fileName = fileName.replace("_", " "); - } else { - fileName = fileName.replace(" ", "_"); - } - return fileName; - } - protected void bookToYml(String path, String fileName, BookMeta book) throws IOException { FileConfiguration bookYml = YamlConfiguration.loadConfiguration(new File(path, "blank")); @@ -650,7 +427,7 @@ public class BooksWithoutBorders extends JavaPlugin { return null; } - List pages = new ArrayList<>(cleanList(rawPages)); + List pages = new ArrayList<>(InputCleaningHelper.cleanList(rawPages)); bDat.setAuthor(author); bDat.setTitle(title); @@ -816,10 +593,90 @@ public class BooksWithoutBorders extends JavaPlugin { return book; } - protected Boolean eSave(Player player, BookMeta book, String key) { + /** + * Makes the player pay for printing a given number of books + * + * @param player

The player printing the books

+ * @param numCopies

The number of copies the player is trying to print

+ * @return

True if the player cannot pay for the printing of the books

+ */ + public boolean cannotPayForBookPrinting(Player player, int numCopies) { + //BookPriceQuantity: How many items are required to pay for each book + //BookPriceType: Which item is used to pay for the books. AIR = use economy + double cost = BooksWithoutBorders.bookPriceQuantity * numCopies; + int itemCost = (int) cost; + + if (BooksWithoutBorders.bookPriceType == Material.AIR) { + return !payForBookPrintingEconomy(player, cost, numCopies); + } else { + if (player.getInventory().contains(BooksWithoutBorders.bookPriceType, itemCost)) { + payForBookPrintingItem(player, itemCost); + return false; + } else { + BooksWithoutBorders.sendErrorMessage(player, itemCost + " " + BooksWithoutBorders.bookPriceType.toString() + + "(s) are required for this command!"); + return true; + } + } + } + + /** + * Uses economy to take payment for printing a number of books + * + * @param player

The player which needs to pay

+ * @param cost

The cost of the book printing

+ * @param numCopies

The number of books the player is printing

+ * @return

True if the player had the money and it has been withdrawn

+ */ + private boolean payForBookPrintingEconomy(Player player, double cost, int numCopies) { + Economy economy = BooksWithoutBorders.eco; + if ((economy.getBalance(player) - cost) >= 0) { + economy.withdrawPlayer(player, cost); + BooksWithoutBorders.sendSuccessMessage(player, economy.format(cost) + " withdrawn to create " + numCopies + " book(s)"); + BooksWithoutBorders.sendSuccessMessage(player, "New balance: " + economy.format(economy.getBalance(player))); + return true; + } else { + BooksWithoutBorders.sendErrorMessage(player, economy.format(cost) + " is required for this command!"); + return false; + } + } + + /** + * Takes payment for printing a number of books by withdrawing the correct item + * + * @param player

The player which needs to pay

+ * @param itemCost

The number of items to pay

+ */ + private void payForBookPrintingItem(Player player, int itemCost) { + PlayerInventory playerInventory = player.getInventory(); + + int clearedAmount = 0; + while (clearedAmount < itemCost) { + int firstItemIndex = playerInventory.first(BooksWithoutBorders.bookPriceType); + ItemStack firstItem = playerInventory.getItem(firstItemIndex); + + if (Objects.requireNonNull(firstItem).getAmount() <= itemCost - clearedAmount) { + clearedAmount += firstItem.getAmount(); + player.getInventory().clear(firstItemIndex); + } else { + clearedAmount = itemCost; + firstItem.setAmount(firstItem.getAmount() - (clearedAmount)); + } + } + } + + /** + * Saves an encrypted book to be decryptable for the given user + * + * @param player

The player encrypting the book

+ * @param bookMetaData

Metadata for the book to encrypt

+ * @param key

The key to use for encryption

+ * @return

The new encrypted metadata for the book, or null if encryption failed

+ */ + protected Boolean saveEncryptedBook(Player player, BookMeta bookMetaData, String key) { String path = bookFolder + "Encrypted" + SLASH; - String fileName = (!book.hasTitle()) ? "Untitled," + player.getName() : - book.getTitle() + titleAuthorSeparator + book.getAuthor(); + String fileName = (!bookMetaData.hasTitle()) ? "Untitled," + player.getName() : + bookMetaData.getTitle() + titleAuthorSeparator + bookMetaData.getAuthor(); fileName = "[" + key + "]" + fileName; fileName = cleanString(fileName); @@ -832,7 +689,7 @@ public class BooksWithoutBorders extends JavaPlugin { } try { - bookToYml(path, fileName, book); + bookToYml(path, fileName, bookMetaData); } catch (IOException e) { e.printStackTrace(); sendErrorMessage(player, "Encryption failed!"); @@ -841,7 +698,15 @@ public class BooksWithoutBorders extends JavaPlugin { return true; } - protected BookMeta groupESave(Player player, BookMeta bookMetadata, String groupName) { + /** + * Saves an encrypted book to be decryptable for the given group + * + * @param player

The player trying to encrypt the book

+ * @param bookMetadata

Metadata for the book to encrypt

+ * @param groupName

The group which should be able to decrypt the book

+ * @return

The new encrypted metadata for the book, or null if encryption failed

+ */ + protected BookMeta saveEncryptedBookForGroup(Player player, BookMeta bookMetadata, String groupName) { String path = bookFolder + "Encrypted" + SLASH + cleanString(groupName) + SLASH; File dirTest = new File(path); //Creates group dir @@ -863,15 +728,15 @@ public class BooksWithoutBorders extends JavaPlugin { fileName = cleanString(fileName); fileName = fixName(fileName, false); - List nuLore = new ArrayList<>(); - nuLore.add(ChatColor.GRAY + "[" + groupName + " encrypted]"); + List newLore = new ArrayList<>(); + newLore.add(ChatColor.GRAY + "[" + groupName + " encrypted]"); if (bookMetadata.hasLore()) { List oldLore = bookMetadata.getLore(); if (oldLore != null) { - nuLore.addAll(oldLore); + newLore.addAll(oldLore); } } - bookMetadata.setLore(nuLore); + bookMetadata.setLore(newLore); //Save file File file = (useYml) ? new File(path + fileName + ".yml") : new File(path + fileName + ".txt"); @@ -888,7 +753,15 @@ public class BooksWithoutBorders extends JavaPlugin { return bookMetadata; } - public ItemStack eLoad(Player player, String key, boolean deleteEncryptedFile) { + /** + * Loads an encrypted book + * + * @param player

The player trying to load the book

+ * @param key

The encryption key/password for decryption

+ * @param deleteEncryptedFile

Whether to delete the plaintext file after decryption is finished

+ * @return

The loaded book, or null if no book could be loaded

+ */ + public ItemStack loadEncryptedBook(Player player, String key, boolean deleteEncryptedFile) { ItemStack heldBook = InventoryHelper.getHeldBook(player, true); BookMeta bookMetadata = (BookMeta) heldBook.getItemMeta(); String path = bookFolder + "Encrypted" + SLASH; @@ -897,7 +770,8 @@ public class BooksWithoutBorders extends JavaPlugin { return null; } - String fileName = (!bookMetadata.hasTitle()) ? "Untitled," + player.getName() : bookMetadata.getTitle() + titleAuthorSeparator + bookMetadata.getAuthor(); + String fileName = (!bookMetadata.hasTitle()) ? "Untitled," + player.getName() : bookMetadata.getTitle() + + titleAuthorSeparator + bookMetadata.getAuthor(); fileName = "[" + key + "]" + fileName; fileName = cleanString(fileName); fileName = fixName(fileName, false); @@ -955,18 +829,6 @@ public class BooksWithoutBorders extends JavaPlugin { return FileHelper.listFiles(sender, file, silent); } - /** - * Removes null and empty items from a list - * - * @param list

The list to clean

- * @return

A clean list containing all relevant values

- */ - protected List cleanList(List list) { - List resultList = new ArrayList<>(list); - resultList.removeIf((item) -> item == null || item.trim().isEmpty()); - return resultList; - } - /** * Encrypts a book * @@ -1016,7 +878,7 @@ public class BooksWithoutBorders extends JavaPlugin { //Format the last page just in case BookFormatter.formatLastPage(encryptedPages); //Remove empty pages - List newPages = cleanList(encryptedPages); + List newPages = InputCleaningHelper.cleanList(encryptedPages); ItemStack encryptedBook = createEncryptedBook(book, newPages, player, newMetadata); @@ -1060,9 +922,9 @@ public class BooksWithoutBorders extends JavaPlugin { BookMeta newMetadata = book; boolean wasSaved; if (groupName.trim().isEmpty()) { - wasSaved = eSave(player, book, integerKey); + wasSaved = saveEncryptedBook(player, book, integerKey); } else { - newMetadata = groupESave(player, book, groupName); + newMetadata = saveEncryptedBookForGroup(player, book, groupName); wasSaved = newMetadata != null; } if (wasSaved) { @@ -1092,67 +954,6 @@ public class BooksWithoutBorders extends JavaPlugin { sender.sendMessage(errorColor + message); } - /** - * Deletes the given book - * - * @param sender

The sender of the command to delete the book

- * @param fileName

The file name of the book

- * @param isPublic

Whether the book to delete is public or not

- */ - public void deleteBook(CommandSender sender, String fileName, Boolean isPublic) { - //If the file name is an index of the load list, load the book - try { - int loadListIndex = Integer.parseInt(fileName); - String senderName = sender.getName(); - if (loadList.containsKey(senderName) && loadListIndex <= loadList.get(senderName).size()) { - fileName = loadList.get(senderName).get(loadListIndex - 1); - } - } catch (NumberFormatException ignored) { - } - - //Get the file to be deleted - File file; - if (isPublic) { - file = FileHelper.getBookFile(bookFolder + fileName); - } else { - file = FileHelper.getBookFile(bookFolder + cleanString(sender.getName()) + SLASH + fileName); - } - - //Send message if no such file could be found - if (file == null) { - sendErrorMessage(sender, "Incorrect file name!"); - return; - } - - //Try to delete the file - try { - if (file.delete()) { - sendSuccessMessage(sender, "\"" + fileName + "\" deleted successfully"); - } else { - sendErrorMessage(sender, "Deletion failed without an exception!"); - } - } catch (Exception e) { - sendErrorMessage(sender, "Deletion failed!"); - } - } - - /** - * Un-signs the player's currently held book - * - * @param player

The player holding the book

- * @param mainHand

Whether the player is holding the book in its main hand or its off hand

- */ - public void unSignHeldBook(Player player, boolean mainHand) { - //Get the old book - BookMeta oldBook = InventoryHelper.getHeldBookMetadata(player, mainHand); - - //UnSign the book - ItemStack newBook = new ItemStack(Material.WRITABLE_BOOK); - newBook.setItemMeta(oldBook); - - InventoryHelper.replaceHeldItem(player, newBook, mainHand); - } - /** * Checks whether books have a price for printing them * @@ -1162,93 +963,4 @@ public class BooksWithoutBorders extends JavaPlugin { return (bookPriceType != null && bookPriceQuantity > 0); } - /** - * Makes the player pay for printing a given number of books - * - * @param player

The player printing the books

- * @param numCopies

The number of copies the player is trying to print

- * @return

True if the player cannot pay for the printing of the books

- */ - public boolean cannotPayForBookPrinting(Player player, int numCopies) { - //BookPriceQuantity: How many items are required to pay for each book - //BookPriceType: Which item is used to pay for the books. AIR = use economy - double cost = bookPriceQuantity * numCopies; - int itemCost = (int) cost; - - if (bookPriceType == Material.AIR) { - return !payForBookPrintingEconomy(player, cost, numCopies); - } else { - if (player.getInventory().contains(bookPriceType, itemCost)) { - payForBookPrintingItem(player, itemCost); - return false; - } else { - sendErrorMessage(player, itemCost + " " + bookPriceType.toString() + - "(s) are required for this command!"); - return true; - } - } - } - - /** - * Takes payment for printing a number of books by withdrawing the correct item - * - * @param player

The player which needs to pay

- * @param itemCost

The number of items to pay

- */ - private void payForBookPrintingItem(Player player, int itemCost) { - PlayerInventory playerInventory = player.getInventory(); - - int clearedAmount = 0; - while (clearedAmount < itemCost) { - int firstItemIndex = playerInventory.first(bookPriceType); - ItemStack firstItem = playerInventory.getItem(firstItemIndex); - - if (Objects.requireNonNull(firstItem).getAmount() <= itemCost - clearedAmount) { - clearedAmount += firstItem.getAmount(); - player.getInventory().clear(firstItemIndex); - } else { - clearedAmount = itemCost; - firstItem.setAmount(firstItem.getAmount() - (clearedAmount)); - } - } - } - - /** - * Uses economy to take payment for printing a number of books - * - * @param player

The player which needs to pay

- * @param cost

The cost of the book printing

- * @param numCopies

The number of books the player is printing

- * @return

True if the player had the money and it has been withdrawn

- */ - private boolean payForBookPrintingEconomy(Player player, double cost, int numCopies) { - Economy economy = BooksWithoutBorders.eco; - if ((economy.getBalance(player) - cost) >= 0) { - economy.withdrawPlayer(player, cost); - sendSuccessMessage(player, economy.format(cost) + " withdrawn to create " + numCopies + " book(s)"); - sendSuccessMessage(player, "New balance: " + economy.format(economy.getBalance(player))); - return true; - } else { - sendErrorMessage(player, economy.format(cost) + " is required for this command!"); - return false; - } - } - - /** - * Checks whether the given player is the author of a given book - * - * @param player

The player to check

- * @param book

The book to check

- * @return

True if the player is the book's author

- */ - public boolean isAuthor(Player player, BookMeta book) { - String author = book.getAuthor(); - if (author != null && cleanString(player.getName()).equalsIgnoreCase(cleanString(author))) { - return true; - } - - sendErrorMessage(player, "You must be the author of this book to use this command!"); - return false; - } - } diff --git a/src/main/java/net/knarcraft/bookswithoutborders/BooksWithoutBordersListener.java b/src/main/java/net/knarcraft/bookswithoutborders/BooksWithoutBordersListener.java index 12ba8bc..f6b45ee 100644 --- a/src/main/java/net/knarcraft/bookswithoutborders/BooksWithoutBordersListener.java +++ b/src/main/java/net/knarcraft/bookswithoutborders/BooksWithoutBordersListener.java @@ -7,6 +7,7 @@ import java.util.Objects; import net.knarcraft.bookswithoutborders.state.EncryptionStyle; import net.knarcraft.bookswithoutborders.utility.EncryptionHelper; import net.knarcraft.bookswithoutborders.utility.FileHelper; +import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper; import net.knarcraft.bookswithoutborders.utility.InventoryHelper; import org.bukkit.ChatColor; import org.bukkit.Material; @@ -58,7 +59,7 @@ public class BooksWithoutBordersListener implements Listener { fileName = oldBook.getTitle() + BooksWithoutBorders.titleAuthorSeparator + oldBook.getAuthor(); } - String cleanPlayerName = BooksWithoutBorders.bwb.cleanString(player.getName()); + String cleanPlayerName = InputCleaningHelper.cleanString(player.getName()); String[] possiblePaths = new String[]{ bookFolder + fileName + ".yml", @@ -359,7 +360,7 @@ public class BooksWithoutBordersListener implements Listener { String fileName = oldBook.getTitle() + BooksWithoutBorders.titleAuthorSeparator + oldBook.getAuthor(); - String encryptionFile = BooksWithoutBorders.bwb.cleanString(groupName) + slash + fileName + ".yml"; + String encryptionFile = InputCleaningHelper.cleanString(groupName) + slash + fileName + ".yml"; File file = new File(bookFolder + "Encrypted" + slash + encryptionFile); if (!file.isFile()) { @@ -446,7 +447,7 @@ public class BooksWithoutBordersListener implements Listener { String lineText = ChatColor.stripColor(sign.getLine(2)); String key = EncryptionHelper.getNumberKeyFromStringKey(lineText); - ItemStack book = BooksWithoutBorders.bwb.eLoad(player, key, false); + ItemStack book = BooksWithoutBorders.bwb.loadEncryptedBook(player, key, false); if (book != null) { player.getInventory().setItem(hand, book); player.sendMessage(ChatColor.GREEN + "Book decrypted!"); diff --git a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandBooksWithoutBorders.java b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandBooksWithoutBorders.java index c2469a9..c92fd50 100644 --- a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandBooksWithoutBorders.java +++ b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandBooksWithoutBorders.java @@ -1,12 +1,150 @@ package net.knarcraft.bookswithoutborders.command; +import net.knarcraft.bookswithoutborders.BooksWithoutBorders; +import org.bukkit.Material; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import static net.knarcraft.bookswithoutborders.BooksWithoutBorders.bookPriceQuantity; +import static net.knarcraft.bookswithoutborders.BooksWithoutBorders.bookPriceType; +import static net.knarcraft.bookswithoutborders.BooksWithoutBorders.commandColor; +import static net.knarcraft.bookswithoutborders.BooksWithoutBorders.sendErrorMessage; +import static net.knarcraft.bookswithoutborders.BooksWithoutBorders.sendSuccessMessage; +import static net.knarcraft.bookswithoutborders.BooksWithoutBorders.successColor; + +/** + * Command executor for the books without borders (bwb) command + */ public class CommandBooksWithoutBorders implements CommandExecutor { + + private final BooksWithoutBorders booksWithoutBorders; + + public CommandBooksWithoutBorders(BooksWithoutBorders booksWithoutBorders) { + this.booksWithoutBorders = booksWithoutBorders; + } + @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - return false; + if (sender instanceof Player) { + showPlayerCommands(sender); + } else { + showConsoleCommands(sender); + } + return true; } + + /** + * Shows all commands available to the console + * @param sender

The console which sent the command

+ */ + private void showConsoleCommands(CommandSender sender) { + sender.sendMessage(commandColor + "Use: /bwb [Command]"); + sender.sendMessage(commandColor + "[] denote parameters"); + sender.sendMessage(commandColor + "Commands:"); + sender.sendMessage(commandColor + "\nReload:" + successColor + " Reloads BwB's config file"); + sender.sendMessage(commandColor + "givePublic [file name or number] [player name] [true/false]: " + successColor); + sendSuccessMessage(sender, "Gives the selected player a book from the public directory"); + sendSuccessMessage(sender, "If no file is specified, a list of available files is returned"); + sender.sendMessage(commandColor + "deletePublic [file name or number]: " + successColor + "Deletes the specified"); + sendSuccessMessage(sender, "file in the public directory"); + sendSuccessMessage(sender, "If no file is specified, a list of available files is returned"); + } + + /** + * Shows all commands available to the sending player + * @param sender

The player which sent the command

+ */ + private void showPlayerCommands(CommandSender sender) { + //Lists all commands + sender.sendMessage(commandColor + "Use: /bwb [Command]"); + sender.sendMessage(commandColor + "[] denote parameters"); + + if (booksWithoutBorders.booksHavePrice()) { + if (bookPriceType != Material.AIR) { + sendErrorMessage(sender, "[" + (int) bookPriceQuantity + " " + bookPriceType.toString() + "(s) are required to create a book]"); + } else { + sendErrorMessage(sender, "[" + BooksWithoutBorders.eco.format(bookPriceQuantity) + " is required to create a book]"); + } + } + sender.sendMessage(commandColor + "Commands:"); + + if (sender.hasPermission("bookswithoutborders.load")) { + sender.sendMessage(commandColor + "\nLoad [file name or number] [# of copies] [true/false]:"); + sendSuccessMessage(sender, "Creates a book from the specified file and gives it to the player"); + sendSuccessMessage(sender, "If no file is specified, a list of available files is returned"); + sendSuccessMessage(sender, "If true is specified the book will be signed, if false it will be"); + sendSuccessMessage(sender, "unsigned"); + } + if (sender.hasPermission("bookswithoutborders.loadPublic")) { + sender.sendMessage(commandColor + "loadPublic [file name or number] [# of copies] [true/false]:"); + sendSuccessMessage(sender, "Same as Load, but views files in the public directory"); + } + if (sender.hasPermission("bookswithoutborders.save")) { + sender.sendMessage("\n" + commandColor + "Save [true/false]: " + successColor + "Saves the book the player is"); + sendSuccessMessage(sender, "holding to a text file in a private directory"); + sendSuccessMessage(sender, "If true is specified, a book of the same name by the same"); + sendSuccessMessage(sender, "author will be overwritten by the new book"); + } + if (sender.hasPermission("bookswithoutborders.savePublic")) { + sender.sendMessage(commandColor + "savePublic [true/false]: " + successColor + "Same as Save,"); + sendSuccessMessage(sender, "but saves files in the public directory"); + } + if (sender.hasPermission("bookswithoutborders.give")) { + sender.sendMessage("\n" + commandColor + "Give [file name or number] [player name] [# of copies] [true/false]:"); + sendSuccessMessage(sender, "Gives the selected player a book from your personal directory"); + } + if (sender.hasPermission("bookswithoutborders.givePublic")) { + sender.sendMessage(commandColor + "givePublic [file name or number] [player name] [# of copies] [true/false]:"); + sendSuccessMessage(sender, "Same as give, but uses books from the public directory"); + } + if (sender.hasPermission("bookswithoutborders.delete")) { + sender.sendMessage(commandColor + "\nDelete [file name or number]: " + successColor + "Deletes the specified"); + sendSuccessMessage(sender, "file in the player's directory"); + sendSuccessMessage(sender, "If no file is specified, a list of available files is returned"); + } + if (sender.hasPermission("bookswithoutborders.admin")) { + sender.sendMessage(commandColor + "deletePublic [file name or number]: " + successColor + "Same as Delete,"); + sendSuccessMessage(sender, "but deletes files in the public directory"); + sender.sendMessage(commandColor + "\nReload:" + successColor + " Reloads BwB's configuration file"); + } + + if (sender.hasPermission("bookswithoutborders.unsign")) { + sender.sendMessage("\n" + commandColor + "Unsign: " + successColor + "Un-signs the book the player is holding"); + } + if (sender.hasPermission("bookswithoutborders.copy")) { + sender.sendMessage("\n" + commandColor + "Copy [number of copies]: " + successColor + "Copies the book the player is holding"); + } + + if (sender.hasPermission("bookswithoutborders.encrypt")) { + sender.sendMessage("\n" + commandColor + "Encrypt [key] [style]: " + successColor + "Encrypts the book the player is holding"); + sender.sendMessage(commandColor + "[key]" + successColor + " is required and can be any phrase or number excluding spaces"); + sender.sendMessage(commandColor + "[style]" + successColor + " is not required. Possible values are \"DNA\" or \"Magic\""); + } + if (sender.hasPermission("bookswithoutborders.groupEncrypt")) { + sender.sendMessage("\n" + commandColor + "groupEncrypt [group name] [key] [style]: " + successColor + "Encrypts book so that only players with the" + + "\n bookswithoutborders.decrypt." + commandColor + "[group name]" + successColor + " permission may decrypt the book by holding and left clicking the book"); + } + if (sender.hasPermission("bookswithoutborders.decrypt")) { + sender.sendMessage("\n" + commandColor + "Decrypt [key]: " + successColor + "Decrypts the book the player is holding"); + sender.sendMessage(commandColor + "[key]" + successColor + " is required and MUST be IDENTICAL to the key used to encrypt held book"); + } + if (sender.hasPermission("bookswithoutborders.setTitle")) { + sender.sendMessage("\n" + commandColor + "setTitle [title]: " + successColor + "Sets the title of the book/item the player is holding"); + } + if (sender.hasPermission("bookswithoutborders.setAuthor")) { + sender.sendMessage("\n" + commandColor + "setAuthor [author]: " + successColor + "Sets the author of the book the player is holding"); + } + if (sender.hasPermission("bookswithoutborders.setLore")) { + sender.sendMessage("\n" + commandColor + "setLore [lore]: " + successColor + "Sets the lore of the item the player is holding"); + sendSuccessMessage(sender, "Insert the lore_line_separator character to force a new line\n[\"~\" by default]"); + } + if (sender.hasPermission("bookswithoutborders.setBookPrice")) { + sender.sendMessage("\n" + commandColor + "setBookPrice [Item/Eco] [quantity]: " + successColor + "Sets the per-book-price to create a book via commands." + + "\nIf [Item], the item in the player's hand in the amount of [quantity] will be the price.\nIf [Eco], a Vault based economy will be used for price." + + "\nIf neither [Item/Eco] or [quantity] are specified the current price to create books will be removed."); + } + } + } diff --git a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandCopy.java b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandCopy.java index eab3334..f92856e 100644 --- a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandCopy.java +++ b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandCopy.java @@ -1,6 +1,7 @@ package net.knarcraft.bookswithoutborders.command; import net.knarcraft.bookswithoutborders.BooksWithoutBorders; +import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper; import net.knarcraft.bookswithoutborders.utility.InventoryHelper; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; @@ -45,7 +46,7 @@ public class CommandCopy implements CommandExecutor { int copies = Integer.parseInt(args[0]); if (copies > 0) { if (BooksWithoutBorders.authorOnlyCopy && !player.hasPermission("bookswithoutborders.bypassAuthorOnlyCopy")) { - if (!booksWithoutBorders.isAuthor(player, (BookMeta) Objects.requireNonNull(heldBook.getItemMeta()))) + if (!isAuthor(player, (BookMeta) Objects.requireNonNull(heldBook.getItemMeta()))) return false; } @@ -70,4 +71,22 @@ public class CommandCopy implements CommandExecutor { return true; } + /** + * Checks whether the given player is the author of a given book + * + * @param player

The player to check

+ * @param book

The book to check

+ * @return

True if the player is the book's author

+ */ + private boolean isAuthor(Player player, BookMeta book) { + String author = book.getAuthor(); + String playerName = InputCleaningHelper.cleanString(player.getName()); + if (author != null && playerName.equalsIgnoreCase(InputCleaningHelper.cleanString(author))) { + return true; + } + + BooksWithoutBorders.sendErrorMessage(player, "You must be the author of this book to use this command!"); + return false; + } + } diff --git a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandDecrypt.java b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandDecrypt.java index be27b10..29a4cea 100644 --- a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandDecrypt.java +++ b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandDecrypt.java @@ -71,7 +71,7 @@ public class CommandDecrypt implements CommandExecutor { if (!key.equalsIgnoreCase("")) { //Decrypt the book - ItemStack book = booksWithoutBorders.eLoad(player, key, false); + ItemStack book = booksWithoutBorders.loadEncryptedBook(player, key, false); if (book != null) { InventoryHelper.setHeldWrittenBook(player, book); BooksWithoutBorders.sendSuccessMessage(player, "Book auto-decrypted!"); @@ -91,7 +91,7 @@ public class CommandDecrypt implements CommandExecutor { String key = EncryptionHelper.getNumberKeyFromStringKey(args[0]); //Decrypt the book - ItemStack book = booksWithoutBorders.eLoad(player, key, true); + ItemStack book = booksWithoutBorders.loadEncryptedBook(player, key, true); if (book != null) { InventoryHelper.setHeldWrittenBook(player, book); BooksWithoutBorders.sendSuccessMessage(player, "Book decrypted!"); diff --git a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandDelete.java b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandDelete.java index 95bb2a9..92a2eb7 100644 --- a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandDelete.java +++ b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandDelete.java @@ -1,11 +1,15 @@ package net.knarcraft.bookswithoutborders.command; import net.knarcraft.bookswithoutborders.BooksWithoutBorders; +import net.knarcraft.bookswithoutborders.utility.FileHelper; +import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import java.io.File; + /** * Command executor for the delete command */ @@ -47,7 +51,7 @@ public class CommandDelete implements CommandExecutor { return false; } else { if (!BooksWithoutBorders.loadList.get(sender.getName()).isEmpty()) { - booksWithoutBorders.deleteBook(sender, args[0], deletePublic); + performBookDeletion(sender, args[0], deletePublic); return true; } else { BooksWithoutBorders.sendErrorMessage(sender, "No files available to delete!"); @@ -59,4 +63,49 @@ public class CommandDelete implements CommandExecutor { return false; } + /** + * Deletes the given book + * + * @param sender

The sender of the command to delete the book

+ * @param fileName

The file name of the book

+ * @param isPublic

Whether the book to delete is public or not

+ */ + public void performBookDeletion(CommandSender sender, String fileName, Boolean isPublic) { + //If the file name is an index of the load list, load the book + try { + int loadListIndex = Integer.parseInt(fileName); + String senderName = sender.getName(); + if (BooksWithoutBorders.loadList.containsKey(senderName) && loadListIndex <= BooksWithoutBorders.loadList.get(senderName).size()) { + fileName = BooksWithoutBorders.loadList.get(senderName).get(loadListIndex - 1); + } + } catch (NumberFormatException ignored) { + } + + //Get the file to be deleted + File file; + if (isPublic) { + file = FileHelper.getBookFile(BooksWithoutBorders.bookFolder + fileName); + } else { + file = FileHelper.getBookFile(BooksWithoutBorders.bookFolder + + InputCleaningHelper.cleanString(sender.getName()) + BooksWithoutBorders.SLASH + fileName); + } + + //Send message if no such file could be found + if (file == null) { + BooksWithoutBorders.sendErrorMessage(sender, "Incorrect file name!"); + return; + } + + //Try to delete the file + try { + if (file.delete()) { + BooksWithoutBorders.sendSuccessMessage(sender, "\"" + fileName + "\" deleted successfully"); + } else { + BooksWithoutBorders.sendErrorMessage(sender, "Deletion failed without an exception!"); + } + } catch (Exception e) { + BooksWithoutBorders.sendErrorMessage(sender, "Deletion failed!"); + } + } + } diff --git a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandGive.java b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandGive.java index 5387a8d..5dde470 100644 --- a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandGive.java +++ b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandGive.java @@ -1,6 +1,7 @@ package net.knarcraft.bookswithoutborders.command; import net.knarcraft.bookswithoutborders.BooksWithoutBorders; +import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; @@ -68,7 +69,7 @@ public class CommandGive implements CommandExecutor { return false; } - String bookToLoad = booksWithoutBorders.cleanString(bookIdentifier); + String bookToLoad = InputCleaningHelper.cleanString(bookIdentifier); try { ItemStack newBook = booksWithoutBorders.loadBook(player, bookToLoad, isSigned, "player", Integer.parseInt(copies)); if (newBook != null) { diff --git a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandGivePublic.java b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandGivePublic.java index 09cb6f8..8a5bc84 100644 --- a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandGivePublic.java +++ b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandGivePublic.java @@ -1,12 +1,87 @@ package net.knarcraft.bookswithoutborders.command; +import net.knarcraft.bookswithoutborders.BooksWithoutBorders; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.cleanString; + +/** + * Command executor for the give public command + */ public class CommandGivePublic implements CommandExecutor { + + private final BooksWithoutBorders booksWithoutBorders; + + public CommandGivePublic(BooksWithoutBorders booksWithoutBorders) { + this.booksWithoutBorders = booksWithoutBorders; + } + @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - return false; + if (sender instanceof Player) { + if (!sender.hasPermission("bookswithoutborders.givePublic")) { + BooksWithoutBorders.sendErrorMessage(sender, " You don't have permission to use this command!"); + return false; + } + } + + if (args.length == 2 || args.length > 5) { + BooksWithoutBorders.sendErrorMessage(sender, "Incorrect number of arguments for this command!"); + return false; + } + + if (args.length == 1) { + BooksWithoutBorders.loadList.put(sender.getName(), booksWithoutBorders.listFiles(sender, true, false)); + return true; + } + + for (int x = 0; x < args[1].length(); x++) { + if (!Character.isDigit(args[1].charAt(x))) + break; + if (x == args[1].length() - 1) + BooksWithoutBorders.loadList.put(sender.getName(), booksWithoutBorders.listFiles(sender, true, true)); + } + + ItemStack newBook; + Player receivingPlayer = booksWithoutBorders.getServer().getPlayer(args[2]); + if (receivingPlayer == null) { + BooksWithoutBorders.sendErrorMessage(sender, "Player not found!"); + return false; + } + + if (receivingPlayer.getInventory().firstEmpty() == -1) { + BooksWithoutBorders.sendErrorMessage(sender, "Receiving player must have space in their inventory to receive books!"); + return false; + } + //bwb give [book name] [player] [numCopies] [is signed] + try { + if (args.length == 5) + newBook = booksWithoutBorders.loadBook(sender, cleanString(args[1]), args[4], "public", Integer.parseInt(args[3])); + else if (args.length == 4) { + if (args[3].equalsIgnoreCase("true") || args[3].equalsIgnoreCase("false")) + newBook = booksWithoutBorders.loadBook(sender, cleanString(args[1]), args[3], "public"); + else + newBook = booksWithoutBorders.loadBook(sender, cleanString(args[1]), "true", "public", Integer.parseInt(args[3])); + } else + newBook = booksWithoutBorders.loadBook(sender, cleanString(args[1]), "true", "public"); + + if (newBook != null) { + receivingPlayer.getInventory().addItem(newBook); + BooksWithoutBorders.sendSuccessMessage(sender, "Book sent!"); + BooksWithoutBorders.sendSuccessMessage(receivingPlayer, "Book received!"); + return true; + } else { + BooksWithoutBorders.sendErrorMessage(sender, "Book failed to load!"); + return false; + } + } catch (NumberFormatException e) { + BooksWithoutBorders.sendErrorMessage(sender, "Invalid number of book copies specified!"); + return false; + } } + } diff --git a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandLoad.java b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandLoad.java index 62b8ecb..2d96e07 100644 --- a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandLoad.java +++ b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandLoad.java @@ -1,6 +1,7 @@ package net.knarcraft.bookswithoutborders.command; import net.knarcraft.bookswithoutborders.BooksWithoutBorders; +import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; @@ -71,7 +72,7 @@ public class CommandLoad implements CommandExecutor { BooksWithoutBorders.loadList.put(player.getName(), booksWithoutBorders.listFiles(player, loadPublic, true)); } catch (NumberFormatException ignored) {} - String bookToLoad = booksWithoutBorders.cleanString(bookIdentifier); + String bookToLoad = InputCleaningHelper.cleanString(bookIdentifier); try { //Give the new book if it cannot be loaded ItemStack newBook = booksWithoutBorders.loadBook(player, bookToLoad, isSigned, directory, Integer.parseInt(copies)); diff --git a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandReload.java b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandReload.java new file mode 100644 index 0000000..a4757b4 --- /dev/null +++ b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandReload.java @@ -0,0 +1,30 @@ +package net.knarcraft.bookswithoutborders.command; + +import net.knarcraft.bookswithoutborders.BooksWithoutBorders; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; + +/** + * Command executor for the reload command + */ +public class CommandReload implements CommandExecutor { + + private final BooksWithoutBorders booksWithoutBorders; + + public CommandReload(BooksWithoutBorders booksWithoutBorders) { + this.booksWithoutBorders = booksWithoutBorders; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (booksWithoutBorders.loadConfig() && booksWithoutBorders.loadExistingPlayers()) { + BooksWithoutBorders.sendSuccessMessage(sender, "BooksWithoutBorders configuration reloaded!"); + } else { + BooksWithoutBorders.sendErrorMessage(sender, "Reload Failed!"); + BooksWithoutBorders.sendErrorMessage(sender, "See console for details"); + } + return true; + } + +} diff --git a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandUnSign.java b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandUnSign.java index 53ec7de..3a76f4a 100644 --- a/src/main/java/net/knarcraft/bookswithoutborders/command/CommandUnSign.java +++ b/src/main/java/net/knarcraft/bookswithoutborders/command/CommandUnSign.java @@ -3,22 +3,19 @@ package net.knarcraft.bookswithoutborders.command; import net.knarcraft.bookswithoutborders.BooksWithoutBorders; import net.knarcraft.bookswithoutborders.state.ItemSlot; import net.knarcraft.bookswithoutborders.utility.InventoryHelper; +import org.bukkit.Material; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BookMeta; /** * Command executor for the unsign command */ public class CommandUnSign implements CommandExecutor { - private final BooksWithoutBorders booksWithoutBorders; - - public CommandUnSign(BooksWithoutBorders booksWithoutBorders) { - this.booksWithoutBorders = booksWithoutBorders; - } - @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { if (!(sender instanceof Player player)) { @@ -33,7 +30,24 @@ public class CommandUnSign implements CommandExecutor { //Find which hand the player is using to hold the book. If holding one in each, throw an error ItemSlot holdingSlot = InventoryHelper.getHeldSlotBook(player, false, false, true, true); - booksWithoutBorders.unSignHeldBook(player, holdingSlot == ItemSlot.MAIN_HAND); + unSignHeldBook(player, holdingSlot == ItemSlot.MAIN_HAND); return true; } + + /** + * Un-signs the player's currently held book + * + * @param player

The player holding the book

+ * @param mainHand

Whether the player is holding the book in its main hand or its off hand

+ */ + public void unSignHeldBook(Player player, boolean mainHand) { + //Get the old book + BookMeta oldBook = InventoryHelper.getHeldBookMetadata(player, mainHand); + + //UnSign the book + ItemStack newBook = new ItemStack(Material.WRITABLE_BOOK); + newBook.setItemMeta(oldBook); + + InventoryHelper.replaceHeldItem(player, newBook, mainHand); + } } diff --git a/src/main/java/net/knarcraft/bookswithoutborders/command/GiveTabCompleter.java b/src/main/java/net/knarcraft/bookswithoutborders/command/GiveTabCompleter.java index 88faf30..31db6b0 100644 --- a/src/main/java/net/knarcraft/bookswithoutborders/command/GiveTabCompleter.java +++ b/src/main/java/net/knarcraft/bookswithoutborders/command/GiveTabCompleter.java @@ -11,6 +11,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +/** + * Tab completer for the save command + */ public class GiveTabCompleter implements TabCompleter { final BooksWithoutBorders booksWithoutBorders; diff --git a/src/main/java/net/knarcraft/bookswithoutborders/utility/InputCleaningHelper.java b/src/main/java/net/knarcraft/bookswithoutborders/utility/InputCleaningHelper.java new file mode 100644 index 0000000..d7259d4 --- /dev/null +++ b/src/main/java/net/knarcraft/bookswithoutborders/utility/InputCleaningHelper.java @@ -0,0 +1,55 @@ +package net.knarcraft.bookswithoutborders.utility; + +import java.util.ArrayList; +import java.util.List; + +public class InputCleaningHelper { + + /** + * Removes null and empty items from a list + * + * @param list

The list to clean

+ * @return

A clean list containing all relevant values

+ */ + public static List cleanList(List list) { + List resultList = new ArrayList<>(list); + resultList.removeIf((item) -> item == null || item.trim().isEmpty()); + return resultList; + } + + /** + * Removes any special character from a filename + * + * @param fileName

The file name to clean

+ * @return

The cleaned file name

+ */ + public static String cleanString(String fileName) { + fileName = fileName.replace("/", ""); + fileName = fileName.replace("\\", ""); + fileName = fileName.replace("*", ""); + fileName = fileName.replace(":", ""); + fileName = fileName.replace("|", ""); + fileName = fileName.replace("<", ""); + fileName = fileName.replace(">", ""); + fileName = fileName.replace("?", ""); + fileName = fileName.replace("\"", ""); + return fileName; + } + + /** + * Changes spaces to underscores or underscores to spaces, depending on context + * + * @param fileName

The file name to fix

+ * @param isLoading

Whether loading from a file as opposed to saving to a file

+ * @return

The fixed name

+ */ + public static String fixName(String fileName, Boolean isLoading) { + if (isLoading) { + fileName = fileName.replace("_", " "); + } else { + fileName = fileName.replace(" ", "_"); + } + return fileName; + } + +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index e413929..6aa26ed 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -17,6 +17,10 @@ commands: description: Gives the held book to another player usage: / [# of copies (num)] [signed (true/false)] permission: bookswithoutborders.give + givepublic: + description: Gives a public book to a player + usage: / + permission: bookswithoutborders.givepublic decrypt: description: Decrypts the held book usage: / @@ -77,7 +81,16 @@ commands: description: Loads a previously saved book from the public books folder usage: / [copies] [is signed (true/false)] permission: bookswithoutborders.loadpublic + reload: + description: Reloads configuration from disk + usage: / + permission: bookswithoutborders.admin permissions: + bookswithoutborders.*: + description: Grants all permissions + default: op + children: + bookswithoutborders.admin: true bookswithoutborders.admin: description: Grants all permissions default: op