24 Commits
1.3.8 ... dev

Author SHA1 Message Date
3e3b8e7ad2 Fixes some capitalization
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-08-03 17:27:30 +02:00
b2ce31234d Adds ability to set name and lore shown when chiseled bookshelves are peeked
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-08-03 17:23:19 +02:00
e5aaa29c66 Fixes problems in sorting and generating the character indexes
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-08-03 04:45:21 +02:00
f9674568ba Improves the books in shelf description
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-08-03 04:21:05 +02:00
2203037b00 Adds a warning when trying to save books containing the title author separator in title or author
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-08-03 02:40:13 +02:00
6b44ada84a Improves some hover text
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-08-03 02:11:50 +02:00
9f979dd56e Makes the book list's colors more consistent
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-08-03 02:07:38 +02:00
10ffd17c04 Fixes a lot of nullability issues
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-08-03 01:28:20 +02:00
0aff3fad02 Adds book reloading, and fixes a RegEx expression
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-08-03 00:47:36 +02:00
32d31fced6 Fixes som nullability, and alters special character removal somewhat
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-08-02 21:35:26 +02:00
2627407e6b Adds ability to filter books by author
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-08-02 20:00:48 +02:00
175b66465a Moves book index generation to its own class
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-08-02 19:01:15 +02:00
36b57b9191 Removes some debug output, and fixes some formatting
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-08-02 18:07:52 +02:00
0d5ad490ff Fixes sorting and character indexes for filenames with color codes
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-08-02 18:02:33 +02:00
9e300afbef Adds missing spacing when replacing underscores in book names
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-08-02 08:02:09 +02:00
a84a56391a Prevents the § from being used in filenames
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-08-02 07:52:05 +02:00
b15ad18ae3 Makes it easier to manually go to any book page
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-08-02 07:09:07 +02:00
67ccdf3b1d Removes spacing for letter search
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-08-02 06:58:36 +02:00
b9bd686ae9 Removes the unnecessary display of manual command input in the book menu
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-08-02 06:52:23 +02:00
a5be6bb72c Adds ability to easier find books by first letter
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-08-02 06:47:46 +02:00
ed0a750eb4 Improves the book list somewhat
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-08-02 03:33:44 +02:00
6aa422d461 Fixes wrong color on the inactive next button
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-08-01 21:05:03 +02:00
4be023bd63 Adds a ChatComponent-enhanced book list
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-08-01 20:59:16 +02:00
35e98e0f18 Bumps version for development
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2025-07-28 04:24:40 +02:00
54 changed files with 1420 additions and 318 deletions

View File

@@ -6,7 +6,7 @@
<groupId>net.knarcraft</groupId> <groupId>net.knarcraft</groupId>
<artifactId>BooksWithoutBorders</artifactId> <artifactId>BooksWithoutBorders</artifactId>
<version>1.3.8</version> <version>1.3.9-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<licenses> <licenses>

View File

@@ -18,11 +18,13 @@ import net.knarcraft.bookswithoutborders.command.CommandSave;
import net.knarcraft.bookswithoutborders.command.CommandSavePublic; import net.knarcraft.bookswithoutborders.command.CommandSavePublic;
import net.knarcraft.bookswithoutborders.command.CommandSetAuthor; import net.knarcraft.bookswithoutborders.command.CommandSetAuthor;
import net.knarcraft.bookswithoutborders.command.CommandSetBookPrice; import net.knarcraft.bookswithoutborders.command.CommandSetBookPrice;
import net.knarcraft.bookswithoutborders.command.CommandSetBookshelfData;
import net.knarcraft.bookswithoutborders.command.CommandSetGeneration; import net.knarcraft.bookswithoutborders.command.CommandSetGeneration;
import net.knarcraft.bookswithoutborders.command.CommandSetLore; import net.knarcraft.bookswithoutborders.command.CommandSetLore;
import net.knarcraft.bookswithoutborders.command.CommandSetTitle; import net.knarcraft.bookswithoutborders.command.CommandSetTitle;
import net.knarcraft.bookswithoutborders.command.CommandUnSign; import net.knarcraft.bookswithoutborders.command.CommandUnSign;
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig; import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
import net.knarcraft.bookswithoutborders.handler.BookshelfHandler;
import net.knarcraft.bookswithoutborders.listener.BookEventListener; import net.knarcraft.bookswithoutborders.listener.BookEventListener;
import net.knarcraft.bookswithoutborders.listener.BookshelfListener; import net.knarcraft.bookswithoutborders.listener.BookshelfListener;
import net.knarcraft.bookswithoutborders.listener.PlayerEventListener; import net.knarcraft.bookswithoutborders.listener.PlayerEventListener;
@@ -39,12 +41,15 @@ import org.bukkit.inventory.ItemFactory;
import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import java.util.logging.Level; import java.util.logging.Level;
@@ -59,16 +64,20 @@ import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig
public class BooksWithoutBorders extends JavaPlugin { public class BooksWithoutBorders extends JavaPlugin {
private static ItemFactory itemFactory; private static ItemFactory itemFactory;
private static Map<UUID, List<String>> playerBooksList; private static @NotNull Map<UUID, List<String>> playerBooksList = new HashMap<>();
private static List<String> publicBooksList; private static @NotNull List<String> publicBooksList = new ArrayList<>();
private static Map<Character, Integer> publicLetterIndex;
private static Map<UUID, Map<Character, Integer>> playerLetterIndex;
private static BooksWithoutBorders booksWithoutBorders; private static BooksWithoutBorders booksWithoutBorders;
private static ConsoleCommandSender consoleSender; private static ConsoleCommandSender consoleSender;
private static BookshelfHandler bookshelfHandler;
/** /**
* Gets the console sender for printing to the console * Gets the console sender for printing to the console
* *
* @return <p>The console's console sender</p> * @return <p>The console's console sender</p>
*/ */
@NotNull
public static ConsoleCommandSender getConsoleSender() { public static ConsoleCommandSender getConsoleSender() {
return consoleSender; return consoleSender;
} }
@@ -80,36 +89,70 @@ public class BooksWithoutBorders extends JavaPlugin {
* @param getPublic <p>Whether to get available public books</p> * @param getPublic <p>Whether to get available public books</p>
* @return <p>A list of available books</p> * @return <p>A list of available books</p>
*/ */
public static List<String> getAvailableBooks(CommandSender sender, boolean getPublic) { @NotNull
public static List<String> getAvailableBooks(@NotNull CommandSender sender, boolean getPublic) {
if (getPublic) { if (getPublic) {
return new ArrayList<>(publicBooksList); return new ArrayList<>(publicBooksList);
} else if (sender instanceof Player player) { } else if (sender instanceof Player player) {
UUID playerUUID = player.getUniqueId(); UUID playerUUID = player.getUniqueId();
if (!playerBooksList.containsKey(playerUUID)) { if (!playerBooksList.containsKey(playerUUID)) {
List<String> newFiles = BookFileHelper.listFiles(sender, false); List<String> newFiles = BookFileHelper.listFiles(sender, false);
if (newFiles != null) {
playerBooksList.put(playerUUID, newFiles); playerBooksList.put(playerUUID, newFiles);
playerLetterIndex.put(playerUUID, BookFileHelper.populateLetterIndices(newFiles));
} }
return playerBooksList.get(playerUUID); }
return new ArrayList<>(playerBooksList.get(playerUUID));
} else { } else {
return new ArrayList<>(); return new ArrayList<>();
} }
} }
/**
* Gets the letter index map for public books, or a specific player's books
*
* @param playerIndex <p>The player to get the index for, or null for the public index</p>
* @return <p>An index mapping between a character and the first index containing that character</p>
*/
@NotNull
public static Map<Character, Integer> getLetterIndex(@Nullable UUID playerIndex) {
if (playerIndex == null) {
return publicLetterIndex;
} else {
Map<Character, Integer> letterIndex = playerLetterIndex.get(playerIndex);
return Objects.requireNonNullElseGet(letterIndex, HashMap::new);
}
}
/** /**
* Updates available books * Updates available books
* *
* @param sender <p>The sender to update books for</p> * @param sender <p>The sender to update books for</p>
* @param updatePublic <p>Whether to update public books</p> * @param updatePublic <p>Whether to update public books</p>
*/ */
public static void updateBooks(CommandSender sender, boolean updatePublic) { public static void updateBooks(@NotNull CommandSender sender, boolean updatePublic) {
List<String> newFiles = BookFileHelper.listFiles(sender, updatePublic); List<String> newFiles = BookFileHelper.listFiles(sender, updatePublic);
if (newFiles == null) {
return;
}
if (updatePublic) { if (updatePublic) {
publicBooksList = newFiles; publicBooksList = newFiles;
publicLetterIndex = BookFileHelper.populateLetterIndices(newFiles);
} else if (sender instanceof Player player) { } else if (sender instanceof Player player) {
playerBooksList.put(player.getUniqueId(), newFiles); playerBooksList.put(player.getUniqueId(), newFiles);
playerLetterIndex.put(player.getUniqueId(), BookFileHelper.populateLetterIndices(newFiles));
} }
} }
/**
* Clears book data such as per-player lists and per-player character indexes
*/
public static void clearBookData() {
playerBooksList = new HashMap<>();
playerLetterIndex = new HashMap<>();
}
@Override @Override
public void onEnable() { public void onEnable() {
FileConfiguration config = this.getConfig(); FileConfiguration config = this.getConfig();
@@ -123,8 +166,15 @@ public class BooksWithoutBorders extends JavaPlugin {
booksWithoutBorders = this; booksWithoutBorders = this;
consoleSender = this.getServer().getConsoleSender(); consoleSender = this.getServer().getConsoleSender();
playerBooksList = new HashMap<>(); playerBooksList = new HashMap<>();
playerLetterIndex = new HashMap<>();
BooksWithoutBordersConfig.initialize(this); BooksWithoutBordersConfig.initialize(this);
publicBooksList = BookFileHelper.listFiles(consoleSender, true); @Nullable List<String> files = BookFileHelper.listFiles(consoleSender, true);
if (files != null) {
publicBooksList = files;
publicLetterIndex = BookFileHelper.populateLetterIndices(files);
}
bookshelfHandler = new BookshelfHandler();
bookshelfHandler.load();
PluginManager pluginManager = this.getServer().getPluginManager(); PluginManager pluginManager = this.getServer().getPluginManager();
@@ -148,6 +198,7 @@ public class BooksWithoutBorders extends JavaPlugin {
* *
* @return <p>An instance of this plugin</p> * @return <p>An instance of this plugin</p>
*/ */
@NotNull
public static BooksWithoutBorders getInstance() { public static BooksWithoutBorders getInstance() {
return booksWithoutBorders; return booksWithoutBorders;
} }
@@ -178,6 +229,7 @@ public class BooksWithoutBorders extends JavaPlugin {
registerCommand("formatBook", new CommandFormat()); registerCommand("formatBook", new CommandFormat());
registerCommand("setBookGeneration", new CommandSetGeneration()); registerCommand("setBookGeneration", new CommandSetGeneration());
registerCommand("clearBook", new CommandClear()); registerCommand("clearBook", new CommandClear());
registerCommand("setBookshelfData", new CommandSetBookshelfData());
} }
/** /**
@@ -186,7 +238,7 @@ public class BooksWithoutBorders extends JavaPlugin {
* @param commandName <p>The name of the command to register</p> * @param commandName <p>The name of the command to register</p>
* @param executor <p>The executor to register for the command</p> * @param executor <p>The executor to register for the command</p>
*/ */
private void registerCommand(String commandName, CommandExecutor executor) { private void registerCommand(@NotNull String commandName, @NotNull CommandExecutor executor) {
PluginCommand pluginCommand = this.getCommand(commandName); PluginCommand pluginCommand = this.getCommand(commandName);
if (pluginCommand != null) { if (pluginCommand != null) {
pluginCommand.setExecutor(executor); pluginCommand.setExecutor(executor);
@@ -222,11 +274,21 @@ public class BooksWithoutBorders extends JavaPlugin {
return testFileSaving(); return testFileSaving();
} }
/**
* Gets the bookshelf handler
*
* @return <p>The bookshelf handler</p>
*/
public static BookshelfHandler getBookshelfHandler() {
return bookshelfHandler;
}
/** /**
* Gets the server's item factory * Gets the server's item factory
* *
* @return <p>The server's item factory</p> * @return <p>The server's item factory</p>
*/ */
@NotNull
public static ItemFactory getItemFactory() { public static ItemFactory getItemFactory() {
return itemFactory; return itemFactory;
} }
@@ -270,7 +332,7 @@ public class BooksWithoutBorders extends JavaPlugin {
* @param sender <p>The sender to send the message to</p> * @param sender <p>The sender to send the message to</p>
* @param message <p>The message to send</p> * @param message <p>The message to send</p>
*/ */
public static void sendSuccessMessage(CommandSender sender, String message) { public static void sendSuccessMessage(@NotNull CommandSender sender, @NotNull String message) {
sender.sendMessage(getSuccessColor() + message); sender.sendMessage(getSuccessColor() + message);
} }
@@ -280,7 +342,7 @@ public class BooksWithoutBorders extends JavaPlugin {
* @param sender <p>The sender to send the message to</p> * @param sender <p>The sender to send the message to</p>
* @param message <p>The message to send</p> * @param message <p>The message to send</p>
*/ */
public static void sendErrorMessage(CommandSender sender, String message) { public static void sendErrorMessage(@NotNull CommandSender sender, @NotNull String message) {
sender.sendMessage(getErrorColor() + message); sender.sendMessage(getErrorColor() + message);
} }

View File

@@ -24,7 +24,7 @@ import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig
public class CommandBooksWithoutBorders implements TabExecutor { public class CommandBooksWithoutBorders implements TabExecutor {
@Override @Override
public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
sender.sendMessage(getCommandColor() + "[] denote optional parameters"); sender.sendMessage(getCommandColor() + "[] denote optional parameters");
sender.sendMessage(getCommandColor() + "<> denote required parameters"); sender.sendMessage(getCommandColor() + "<> denote required parameters");
sender.sendMessage(getCommandColor() + "{} denote required permission"); sender.sendMessage(getCommandColor() + "{} denote required permission");
@@ -42,7 +42,7 @@ public class CommandBooksWithoutBorders implements TabExecutor {
* *
* @param sender <p>The console which sent the command</p> * @param sender <p>The console which sent the command</p>
*/ */
private void showConsoleCommands(CommandSender sender) { private void showConsoleCommands(@NotNull CommandSender sender) {
sender.sendMessage(getCommandColor() + "Commands:"); sender.sendMessage(getCommandColor() + "Commands:");
showCommandInfo("deletePublicBook", sender); showCommandInfo("deletePublicBook", sender);
showCommandInfo("givePublicBook", sender); showCommandInfo("givePublicBook", sender);
@@ -54,7 +54,7 @@ public class CommandBooksWithoutBorders implements TabExecutor {
* *
* @param sender <p>The player which sent the command</p> * @param sender <p>The player which sent the command</p>
*/ */
private void showPlayerCommands(CommandSender sender) { private void showPlayerCommands(@NotNull CommandSender sender) {
//Lists all commands //Lists all commands
Material bookPriceType = BooksWithoutBordersConfig.getBookPriceType(); Material bookPriceType = BooksWithoutBordersConfig.getBookPriceType();
double bookPriceQuantity = BooksWithoutBordersConfig.getBookPriceQuantity(); double bookPriceQuantity = BooksWithoutBordersConfig.getBookPriceQuantity();
@@ -69,6 +69,7 @@ public class CommandBooksWithoutBorders implements TabExecutor {
} }
sender.sendMessage(getCommandColor() + "Commands:"); sender.sendMessage(getCommandColor() + "Commands:");
showCommandInfo("booksWithoutBorders", sender);
showCommandInfo("copyBook", sender); showCommandInfo("copyBook", sender);
showCommandInfo("decryptBook", sender); showCommandInfo("decryptBook", sender);
showCommandInfo("deleteBook", sender); showCommandInfo("deleteBook", sender);
@@ -89,6 +90,8 @@ public class CommandBooksWithoutBorders implements TabExecutor {
showCommandInfo("setLore", sender); showCommandInfo("setLore", sender);
showCommandInfo("setTitle", sender); showCommandInfo("setTitle", sender);
showCommandInfo("unsignBook", sender); showCommandInfo("unsignBook", sender);
showCommandInfo("clearBook", sender);
showCommandInfo("setBookshelfData", sender);
} }
/** /**
@@ -97,7 +100,7 @@ public class CommandBooksWithoutBorders implements TabExecutor {
* @param command <p>The command to get information about</p> * @param command <p>The command to get information about</p>
* @param sender <p>The sender asking to see command info</p> * @param sender <p>The sender asking to see command info</p>
*/ */
private void showCommandInfo(String command, CommandSender sender) { private void showCommandInfo(@NotNull String command, @NotNull CommandSender sender) {
PluginCommand pluginCommand = BooksWithoutBorders.getInstance().getCommand(command); PluginCommand pluginCommand = BooksWithoutBorders.getInstance().getCommand(command);
if (pluginCommand != null) { if (pluginCommand != null) {
String permission = pluginCommand.getPermission(); String permission = pluginCommand.getPermission();
@@ -116,7 +119,8 @@ public class CommandBooksWithoutBorders implements TabExecutor {
} }
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
@NotNull String[] arguments) {
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@@ -51,7 +51,7 @@ public class CommandClear implements TabExecutor {
@Nullable @Nullable
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] args) { @NotNull String[] arguments) {
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@@ -26,7 +26,8 @@ import java.util.Objects;
public class CommandCopy implements TabExecutor { public class CommandCopy implements TabExecutor {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] arguments) {
if (!(sender instanceof Player player)) { if (!(sender instanceof Player player)) {
BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!"); BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!");
return false; return false;
@@ -37,14 +38,14 @@ public class CommandCopy implements TabExecutor {
return false; return false;
} }
if (args.length < 1) { if (arguments.length < 1) {
BooksWithoutBorders.sendErrorMessage(player, "You must specify the number of copies to be made!"); BooksWithoutBorders.sendErrorMessage(player, "You must specify the number of copies to be made!");
return false; return false;
} }
try { try {
ItemStack heldBook = InventoryHelper.getHeldBook(player, true); ItemStack heldBook = InventoryHelper.getHeldBook(player, true);
int copies = Integer.parseInt(args[0]); int copies = Integer.parseInt(arguments[0]);
if (copies <= 0) { if (copies <= 0) {
throw new NumberFormatException("Number of copies must be larger than 0"); throw new NumberFormatException("Number of copies must be larger than 0");
} }
@@ -64,7 +65,7 @@ public class CommandCopy implements TabExecutor {
* @param heldBook <p>The book to be copied</p> * @param heldBook <p>The book to be copied</p>
* @return <p>True if the copying was successful</p> * @return <p>True if the copying was successful</p>
*/ */
private boolean performCopy(int copies, Player player, ItemStack heldBook) { private boolean performCopy(int copies, @NotNull Player player, @NotNull ItemStack heldBook) {
//Make sure the player owns the book if authorOnlyCopy is enabled //Make sure the player owns the book if authorOnlyCopy is enabled
if (BooksWithoutBordersConfig.getAuthorOnlyCopy() && if (BooksWithoutBordersConfig.getAuthorOnlyCopy() &&
!player.hasPermission("bookswithoutborders.bypassAuthorOnlyCopy")) { !player.hasPermission("bookswithoutborders.bypassAuthorOnlyCopy")) {
@@ -95,7 +96,7 @@ public class CommandCopy implements TabExecutor {
* @param copies <p>The number of copies to create for the player</p> * @param copies <p>The number of copies to create for the player</p>
* @return <p>True if the payment failed</p> * @return <p>True if the payment failed</p>
*/ */
private boolean paymentUnSuccessful(Player player, int copies) { private boolean paymentUnSuccessful(@NotNull Player player, int copies) {
return BooksWithoutBordersConfig.booksHavePrice() && return BooksWithoutBordersConfig.booksHavePrice() &&
!player.hasPermission("bookswithoutborders.bypassBookPrice") && !player.hasPermission("bookswithoutborders.bypassBookPrice") &&
EconomyHelper.cannotPayForBookPrinting(player, copies); EconomyHelper.cannotPayForBookPrinting(player, copies);
@@ -109,7 +110,7 @@ public class CommandCopy implements TabExecutor {
* @param copies <p>The number of copies requested</p> * @param copies <p>The number of copies requested</p>
* @return <p>True if the book was successfully copied</p> * @return <p>True if the book was successfully copied</p>
*/ */
private boolean copyNextGenerationBook(BookMeta bookMeta, Player player, int copies) { private boolean copyNextGenerationBook(@NotNull BookMeta bookMeta, @NotNull Player player, int copies) {
//Copy the vanilla behavior of refusing copying any further //Copy the vanilla behavior of refusing copying any further
if (bookMeta.getGeneration() == BookMeta.Generation.COPY_OF_COPY || if (bookMeta.getGeneration() == BookMeta.Generation.COPY_OF_COPY ||
bookMeta.getGeneration() == BookMeta.Generation.TATTERED) { bookMeta.getGeneration() == BookMeta.Generation.TATTERED) {
@@ -140,10 +141,10 @@ public class CommandCopy implements TabExecutor {
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
@NotNull String[] args) { @NotNull String[] arguments) {
int argumentCount = args.length; int argumentCount = arguments.length;
if (argumentCount == 1) { if (argumentCount == 1) {
return TabCompletionHelper.filterMatchingStartsWith(TabCompletionTypeHelper.getNumbers(1, 20), args[0]); return TabCompletionHelper.filterMatchingStartsWith(TabCompletionTypeHelper.getNumbers(1, 20), arguments[0]);
} }
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@@ -99,8 +99,9 @@ public class CommandDecrypt implements TabExecutor {
} }
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
int argumentCount = args.length; @NotNull String[] arguments) {
int argumentCount = arguments.length;
if (argumentCount == 1) { if (argumentCount == 1) {
List<String> info = new ArrayList<>(); List<String> info = new ArrayList<>();
info.add("<password>"); info.add("<password>");

View File

@@ -1,6 +1,7 @@
package net.knarcraft.bookswithoutborders.command; package net.knarcraft.bookswithoutborders.command;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders; import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.gui.PagedBookIndex;
import net.knarcraft.bookswithoutborders.state.BookDirectory; import net.knarcraft.bookswithoutborders.state.BookDirectory;
import net.knarcraft.bookswithoutborders.utility.BookFileHelper; import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
import net.knarcraft.bookswithoutborders.utility.BookHelper; import net.knarcraft.bookswithoutborders.utility.BookHelper;
@@ -21,34 +22,35 @@ import java.util.List;
public class CommandDelete implements TabExecutor { public class CommandDelete implements TabExecutor {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] arguments) {
if (!(sender instanceof Player)) { if (!(sender instanceof Player)) {
BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!"); BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!");
return false; return false;
} }
return deleteBook(sender, args, false); return deleteBook(sender, arguments, false);
} }
/** /**
* Deletes a book * Deletes a book
* *
* @param sender <p>The sender trying to delete the book</p> * @param sender <p>The sender trying to delete the book</p>
* @param args <p>The arguments given</p> * @param arguments <p>The arguments given</p>
* @param deletePublic <p>Whether to delete a public book</p> * @param deletePublic <p>Whether to delete a public book</p>
* @return <p>True if the book was deleted successfully</p> * @return <p>True if the book was deleted successfully</p>
*/ */
boolean deleteBook(CommandSender sender, String[] args, boolean deletePublic) { protected boolean deleteBook(@NotNull CommandSender sender, @NotNull String[] arguments, boolean deletePublic) {
//List deletable files String command = deletePublic ? "deletepublicbook" : "deletebook";
if (args.length == 0) { if (PagedBookIndex.displayPage(arguments, sender, deletePublic, command)) {
BookFileHelper.printBooks(sender, deletePublic);
return true; return true;
} }
//Delete the file //Delete the file
if (args.length == 1) { if (arguments.length == 1) {
List<String> availableBooks = BooksWithoutBorders.getAvailableBooks(sender, deletePublic); List<String> availableBooks = BooksWithoutBorders.getAvailableBooks(sender, deletePublic);
if (!availableBooks.isEmpty()) { if (!availableBooks.isEmpty()) {
performBookDeletion(sender, args[0], deletePublic); performBookDeletion(sender, arguments[0], deletePublic);
//Update the book list //Update the book list
BooksWithoutBorders.updateBooks(sender, deletePublic); BooksWithoutBorders.updateBooks(sender, deletePublic);
return true; return true;
@@ -68,7 +70,7 @@ public class CommandDelete implements TabExecutor {
* @param fileName <p>The file name of the book</p> * @param fileName <p>The file name of the book</p>
* @param isPublic <p>Whether the book to delete is public or not</p> * @param isPublic <p>Whether the book to delete is public or not</p>
*/ */
public void performBookDeletion(CommandSender sender, String fileName, Boolean isPublic) { public void performBookDeletion(@NotNull CommandSender sender, @NotNull String fileName, @NotNull Boolean isPublic) {
//If the file name is an index of the load list, load the book //If the file name is an index of the load list, load the book
try { try {
int loadListIndex = Integer.parseInt(fileName); int loadListIndex = Integer.parseInt(fileName);
@@ -103,8 +105,9 @@ public class CommandDelete implements TabExecutor {
} }
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
return doTabCompletion(sender, args, false); @NotNull String[] arguments) {
return doTabCompletion(sender, arguments, false);
} }
/** /**
@@ -115,7 +118,8 @@ public class CommandDelete implements TabExecutor {
* @param deletePublic <p>Whether to delete a public book</p> * @param deletePublic <p>Whether to delete a public book</p>
* @return <p>A list of available arguments</p> * @return <p>A list of available arguments</p>
*/ */
protected List<String> doTabCompletion(CommandSender sender, String[] args, boolean deletePublic) { @NotNull
protected List<String> doTabCompletion(@NotNull CommandSender sender, @NotNull String[] args, boolean deletePublic) {
int argumentCount = args.length; int argumentCount = args.length;
if (argumentCount == 1) { if (argumentCount == 1) {
return TabCompletionHelper.filterMatchingContains(BooksWithoutBorders.getAvailableBooks(sender, deletePublic), return TabCompletionHelper.filterMatchingContains(BooksWithoutBorders.getAvailableBooks(sender, deletePublic),

View File

@@ -13,13 +13,13 @@ import java.util.List;
public class CommandDeletePublic extends CommandDelete implements TabExecutor { public class CommandDeletePublic extends CommandDelete implements TabExecutor {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] arguments) {
return deleteBook(sender, args, true); return deleteBook(sender, arguments, true);
} }
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) { public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] arguments) {
return doTabCompletion(sender, args, true); return doTabCompletion(sender, arguments, true);
} }
} }

View File

@@ -13,6 +13,7 @@ import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.meta.BookMeta;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -23,25 +24,28 @@ import java.util.List;
public class CommandEncrypt implements TabExecutor { public class CommandEncrypt implements TabExecutor {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
if (performPreChecks(sender, args, 1, "You must specify a key to encrypt a book!") == null) { @NotNull String[] arguments) {
if (performPreChecks(sender, arguments, 1, "You must specify a key to encrypt a book!") == null) {
return false; return false;
} }
EncryptionStyle encryptionStyle = args.length == 2 ? EncryptionStyle.getFromString(args[1]) : EncryptionStyle.SUBSTITUTION; EncryptionStyle encryptionStyle = arguments.length == 2 ? EncryptionStyle.getFromString(arguments[1]) : EncryptionStyle.SUBSTITUTION;
return encryptBook(encryptionStyle, (Player) sender, args[0], ""); return encryptBook(encryptionStyle, (Player) sender, arguments[0], "");
} }
/** /**
* Performs necessary pre-checks before going through with the encryption * Performs necessary pre-checks before going through with the encryption
* *
* @param sender <p>The sender trying to encrypt a book</p> * @param sender <p>The sender trying to encrypt a book</p>
* @param args <p>The arguments given</p> * @param arguments <p>The arguments given</p>
* @param necessaryArguments <p>How many arguments is the minimum requirement</p> * @param necessaryArguments <p>How many arguments is the minimum requirement</p>
* @param missingArgumentsError <p>The error to show if a required argument is missing</p> * @param missingArgumentsError <p>The error to show if a required argument is missing</p>
* @return <p>The metadata of the book to encrypt, or null if any checks fail</p> * @return <p>The metadata of the book to encrypt, or null if any checks fail</p>
*/ */
BookMeta performPreChecks(CommandSender sender, String[] args, int necessaryArguments, String missingArgumentsError) { @Nullable
protected BookMeta performPreChecks(@NotNull CommandSender sender, @NotNull String[] arguments,
int necessaryArguments, @NotNull String missingArgumentsError) {
if (!(sender instanceof Player player)) { if (!(sender instanceof Player player)) {
BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!"); BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!");
return null; return null;
@@ -53,7 +57,7 @@ public class CommandEncrypt implements TabExecutor {
return null; return null;
} }
int argumentCount = args.length; int argumentCount = arguments.length;
if (argumentCount < necessaryArguments) { if (argumentCount < necessaryArguments) {
BooksWithoutBorders.sendErrorMessage(player, missingArgumentsError); BooksWithoutBorders.sendErrorMessage(player, missingArgumentsError);
return null; return null;
@@ -85,7 +89,8 @@ public class CommandEncrypt implements TabExecutor {
* @param group <p>The group to encrypt for</p> * @param group <p>The group to encrypt for</p>
* @return <p>True if the book was encrypted successfully</p> * @return <p>True if the book was encrypted successfully</p>
*/ */
boolean encryptBook(EncryptionStyle encryptionStyle, Player player, String key, String group) { protected boolean encryptBook(@NotNull EncryptionStyle encryptionStyle, @NotNull Player player, @NotNull String key,
@NotNull String group) {
ItemSlot heldSlot = InventoryHelper.getHeldSlotBook(player, false, false, true, true); ItemSlot heldSlot = InventoryHelper.getHeldSlotBook(player, false, false, true, true);
ItemStack encryptedBook = EncryptionHelper.encryptBook(player, heldSlot == ItemSlot.MAIN_HAND, key, encryptionStyle, group); ItemStack encryptedBook = EncryptionHelper.encryptBook(player, heldSlot == ItemSlot.MAIN_HAND, key, encryptionStyle, group);
@@ -98,8 +103,10 @@ public class CommandEncrypt implements TabExecutor {
} }
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) { @NotNull
return doTabCompletion(args, false); public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
@NotNull String[] arguments) {
return doTabCompletion(arguments, false);
} }
/** /**
@@ -109,7 +116,8 @@ public class CommandEncrypt implements TabExecutor {
* @param groupEncrypt <p>Whether to auto-complete for group encryption</p> * @param groupEncrypt <p>Whether to auto-complete for group encryption</p>
* @return <p>The strings to auto-complete</p> * @return <p>The strings to auto-complete</p>
*/ */
protected List<String> doTabCompletion(String[] args, boolean groupEncrypt) { @NotNull
protected List<String> doTabCompletion(@NotNull String[] args, boolean groupEncrypt) {
int argumentsCount = args.length; int argumentsCount = args.length;
List<String> encryptionStyles = new ArrayList<>(); List<String> encryptionStyles = new ArrayList<>();

View File

@@ -20,7 +20,8 @@ import java.util.List;
public class CommandFormat implements TabExecutor { public class CommandFormat implements TabExecutor {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] arguments) {
if (!(sender instanceof Player player)) { if (!(sender instanceof Player player)) {
BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!"); BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!");
return false; return false;
@@ -32,7 +33,14 @@ public class CommandFormat implements TabExecutor {
} }
ItemStack heldBook = InventoryHelper.getHeldBook(player, true); ItemStack heldBook = InventoryHelper.getHeldBook(player, true);
heldBook.setItemMeta(BookFormatter.formatPages((BookMeta) heldBook.getItemMeta())); BookMeta meta = (BookMeta) heldBook.getItemMeta();
if (meta == null) {
BooksWithoutBorders.sendErrorMessage(sender, "Unable to get metadata from the held book!");
return false;
}
heldBook.setItemMeta(BookFormatter.formatPages(meta));
BooksWithoutBorders.sendSuccessMessage(sender, "Book formatted!"); BooksWithoutBorders.sendSuccessMessage(sender, "Book formatted!");
@@ -40,7 +48,8 @@ public class CommandFormat implements TabExecutor {
} }
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
@NotNull String[] arguments) {
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@@ -1,7 +1,7 @@
package net.knarcraft.bookswithoutborders.command; package net.knarcraft.bookswithoutborders.command;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders; import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.utility.BookFileHelper; import net.knarcraft.bookswithoutborders.gui.PagedBookIndex;
import net.knarcraft.bookswithoutborders.utility.BookLoader; import net.knarcraft.bookswithoutborders.utility.BookLoader;
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper; import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper;
import net.knarcraft.bookswithoutborders.utility.TabCompletionTypeHelper; import net.knarcraft.bookswithoutborders.utility.TabCompletionTypeHelper;
@@ -13,6 +13,7 @@ import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -25,48 +26,50 @@ public class CommandGive implements TabExecutor {
private final BooksWithoutBorders booksWithoutBorders = BooksWithoutBorders.getInstance(); private final BooksWithoutBorders booksWithoutBorders = BooksWithoutBorders.getInstance();
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] arguments) {
if (!(sender instanceof Player)) { if (!(sender instanceof Player)) {
BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!"); BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!");
return false; return false;
} }
return giveBook(sender, args, false, "player"); return giveBook(sender, arguments, false, "player");
} }
/** /**
* Gives a book to another player * Gives a book to another player
* *
* @param sender <p>The sender trying to give a book</p> * @param sender <p>The sender trying to give a book</p>
* @param args <p>The arguments given</p> * @param arguments <p>The arguments given</p>
* @param givePublic <p>Whether to give a public book</p> * @param givePublic <p>Whether to give a public book</p>
* @param folder <p>The folder containing the book to load</p> * @param folder <p>The folder containing the book to load</p>
* @return <p>True if the book was given successfully</p> * @return <p>True if the book was given successfully</p>
*/ */
boolean giveBook(CommandSender sender, String[] args, boolean givePublic, String folder) { boolean giveBook(@NotNull CommandSender sender, @NotNull String[] arguments, boolean givePublic,
if (args.length == 1 || args.length > 4) { @NotNull String folder) {
String command = givePublic ? "givepublicbook" : "givebook";
if (PagedBookIndex.displayPage(arguments, sender, givePublic, command)) {
return true;
}
if (arguments.length == 1 || arguments.length > 4) {
BooksWithoutBorders.sendErrorMessage(sender, "Incorrect number of arguments for this command!"); BooksWithoutBorders.sendErrorMessage(sender, "Incorrect number of arguments for this command!");
return false; return false;
} }
if (args.length == 0) {
BookFileHelper.printBooks(sender, givePublic);
return true;
}
//Organize and parse input //Organize and parse input
String bookIdentifier = args[0]; String bookIdentifier = arguments[0];
String receivingPlayerName = args[1]; String receivingPlayerName = arguments[1];
String copies = "1"; String copies = "1";
String isSigned = "true"; String isSigned = "true";
if (args.length == 4) { if (arguments.length == 4) {
copies = args[2]; copies = arguments[2];
isSigned = args[3]; isSigned = arguments[3];
} else if (args.length == 3) { } else if (arguments.length == 3) {
if (args[2].equalsIgnoreCase("true") || args[2].equalsIgnoreCase("false")) { if (arguments[2].equalsIgnoreCase("true") || arguments[2].equalsIgnoreCase("false")) {
isSigned = args[2]; isSigned = arguments[2];
} else { } else {
copies = args[2]; copies = arguments[2];
} }
} }
@@ -100,8 +103,9 @@ public class CommandGive implements TabExecutor {
} }
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
return doTabCompletion(sender, args, false); @NotNull String[] arguments) {
return doTabCompletion(sender, arguments, false);
} }
/** /**
@@ -112,7 +116,8 @@ public class CommandGive implements TabExecutor {
* @param listPublic <p>Whether to list public files or player files</p> * @param listPublic <p>Whether to list public files or player files</p>
* @return <p>A list of available choices</p> * @return <p>A list of available choices</p>
*/ */
protected List<String> doTabCompletion(CommandSender sender, String[] args, boolean listPublic) { @Nullable
protected List<String> doTabCompletion(@NotNull CommandSender sender, @NotNull String[] args, boolean listPublic) {
Server server = booksWithoutBorders.getServer(); Server server = booksWithoutBorders.getServer();
int argumentCount = args.length; int argumentCount = args.length;
@@ -157,8 +162,9 @@ public class CommandGive implements TabExecutor {
* @param copies <p>The number of copies the player wants to give</p> * @param copies <p>The number of copies the player wants to give</p>
* @return <p>True if the book was successfully given</p> * @return <p>True if the book was successfully given</p>
*/ */
private boolean loadAndGiveBook(String bookIdentifier, CommandSender sender, Player receivingPlayer, private boolean loadAndGiveBook(@NotNull String bookIdentifier, @NotNull CommandSender sender,
String isSigned, String folder, String copies) throws NumberFormatException { @NotNull Player receivingPlayer, @NotNull String isSigned, @NotNull String folder,
@NotNull String copies) throws NumberFormatException {
String bookToLoad = InputCleaningHelper.cleanString(bookIdentifier); String bookToLoad = InputCleaningHelper.cleanString(bookIdentifier);
ItemStack newBook = BookLoader.loadBook(sender, bookToLoad, isSigned, folder, Integer.parseInt(copies)); ItemStack newBook = BookLoader.loadBook(sender, bookToLoad, isSigned, folder, Integer.parseInt(copies));
if (newBook != null) { if (newBook != null) {

View File

@@ -4,6 +4,7 @@ import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor; import org.bukkit.command.TabExecutor;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List; import java.util.List;
@@ -13,13 +14,16 @@ import java.util.List;
public class CommandGivePublic extends CommandGive implements TabExecutor { public class CommandGivePublic extends CommandGive implements TabExecutor {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
return giveBook(sender, args, true, "public"); @NotNull String[] arguments) {
return giveBook(sender, arguments, true, "public");
} }
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) { @Nullable
return doTabCompletion(sender, args, true); public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
@NotNull String[] arguments) {
return doTabCompletion(sender, arguments, true);
} }
} }

View File

@@ -17,8 +17,9 @@ import java.util.List;
public class CommandGroupEncrypt extends CommandEncrypt implements TabExecutor { public class CommandGroupEncrypt extends CommandEncrypt implements TabExecutor {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
BookMeta bookMetadata = performPreChecks(sender, args, 2, @NotNull String[] arguments) {
BookMeta bookMetadata = performPreChecks(sender, arguments, 2,
"You must specify a group name and key to encrypt a book!"); "You must specify a group name and key to encrypt a book!");
if (bookMetadata == null) { if (bookMetadata == null) {
@@ -32,13 +33,14 @@ public class CommandGroupEncrypt extends CommandEncrypt implements TabExecutor {
return false; return false;
} }
EncryptionStyle encryptionStyle = args.length == 3 ? EncryptionStyle.getFromString(args[2]) : EncryptionStyle.SUBSTITUTION; EncryptionStyle encryptionStyle = arguments.length == 3 ? EncryptionStyle.getFromString(arguments[2]) : EncryptionStyle.SUBSTITUTION;
return encryptBook(encryptionStyle, (Player) sender, args[1], args[0]); return encryptBook(encryptionStyle, (Player) sender, arguments[1], arguments[0]);
} }
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) { public @NotNull List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
return doTabCompletion(args, true); @NotNull String alias, @NotNull String[] arguments) {
return doTabCompletion(arguments, true);
} }
} }

View File

@@ -1,7 +1,7 @@
package net.knarcraft.bookswithoutborders.command; package net.knarcraft.bookswithoutborders.command;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders; import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.utility.BookFileHelper; import net.knarcraft.bookswithoutborders.gui.PagedBookIndex;
import net.knarcraft.bookswithoutborders.utility.BookLoader; import net.knarcraft.bookswithoutborders.utility.BookLoader;
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper; import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper;
import net.knarcraft.bookswithoutborders.utility.TabCompletionTypeHelper; import net.knarcraft.bookswithoutborders.utility.TabCompletionTypeHelper;
@@ -22,20 +22,22 @@ import java.util.List;
public class CommandLoad implements TabExecutor { public class CommandLoad implements TabExecutor {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
return loadBook(sender, args, "player", false); @NotNull String[] arguments) {
return loadBook(sender, arguments, "player", false);
} }
/** /**
* Loads a stored book * Loads a stored book
* *
* @param sender <p>The sender asking to load the book</p> * @param sender <p>The sender asking to load the book</p>
* @param args <p>The arguments given</p> * @param arguments <p>The arguments given</p>
* @param directory <p>The directory to load from (public/player)</p> * @param directory <p>The directory to load from (public/player)</p>
* @param loadPublic <p>Whether to list public files as loadable</p> * @param loadPublic <p>Whether to list public files as loadable</p>
* @return <p>True if the book was loaded successfully</p> * @return <p>True if the book was loaded successfully</p>
*/ */
public boolean loadBook(CommandSender sender, String[] args, String directory, boolean loadPublic) { public boolean loadBook(@NotNull CommandSender sender, @NotNull String[] arguments, @NotNull String directory,
boolean loadPublic) {
if (!(sender instanceof Player player)) { if (!(sender instanceof Player player)) {
BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!"); BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!");
return false; return false;
@@ -46,26 +48,25 @@ public class CommandLoad implements TabExecutor {
return false; return false;
} }
int argumentCount = args.length; int argumentCount = arguments.length;
//Show books available to the player String command = loadPublic ? "loadpublicbook" : "loadbook";
if (argumentCount == 0) { if (PagedBookIndex.displayPage(arguments, sender, loadPublic, command)) {
BookFileHelper.printBooks(sender, loadPublic);
return true; return true;
} }
//Organize and parse input //Organize and parse input
String bookIdentifier = args[0]; String bookIdentifier = arguments[0];
String copies = "1"; String copies = "1";
String isSigned = "true"; String isSigned = "true";
if (args.length == 3) { if (argumentCount == 3) {
copies = args[1]; copies = arguments[1];
isSigned = args[2]; isSigned = arguments[2];
} else if (args.length == 2) { } else if (argumentCount == 2) {
if (args[1].equalsIgnoreCase("true") || args[1].equalsIgnoreCase("false")) { if (arguments[1].equalsIgnoreCase("true") || arguments[1].equalsIgnoreCase("false")) {
isSigned = args[1]; isSigned = arguments[1];
} else { } else {
copies = args[1]; copies = arguments[1];
} }
} }
@@ -95,32 +96,34 @@ public class CommandLoad implements TabExecutor {
} }
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
return doTabCompletion(sender, args, false); @NotNull String[] arguments) {
return doTabCompletion(sender, arguments, false);
} }
/** /**
* Performs the actual tab completion * Performs the actual tab completion
* *
* @param sender <p>The sender of the command</p> * @param sender <p>The sender of the command</p>
* @param args <p>The arguments given</p> * @param arguments <p>The arguments given</p>
* @param loadPublic <p>Whether to list public files or player files</p> * @param loadPublic <p>Whether to list public files or player files</p>
* @return <p>A list of available choices</p> * @return <p>A list of available choices</p>
*/ */
protected List<String> doTabCompletion(CommandSender sender, String[] args, boolean loadPublic) { @NotNull
int argumentCount = args.length; protected List<String> doTabCompletion(@NotNull CommandSender sender, @NotNull String[] arguments, boolean loadPublic) {
int argumentCount = arguments.length;
if (argumentCount == 1) { if (argumentCount == 1) {
//Return list of books //Return list of books
return TabCompletionHelper.filterMatchingContains(BooksWithoutBorders.getAvailableBooks(sender, loadPublic), return TabCompletionHelper.filterMatchingContains(BooksWithoutBorders.getAvailableBooks(sender, loadPublic),
args[0]); arguments[0]);
} else if (argumentCount == 2) { } else if (argumentCount == 2) {
//Number of copies //Number of copies
return TabCompletionHelper.filterMatchingStartsWith(TabCompletionTypeHelper.getBooleansAndNumbers(1, 3), args[1]); return TabCompletionHelper.filterMatchingStartsWith(TabCompletionTypeHelper.getBooleansAndNumbers(1, 3), arguments[1]);
} else if (argumentCount == 3) { } else if (argumentCount == 3) {
//Signed //Signed
try { try {
Integer.parseInt(args[1]); Integer.parseInt(arguments[1]);
return TabCompletionHelper.filterMatchingStartsWith(TabCompletionTypeHelper.getBooleans(), args[2]); return TabCompletionHelper.filterMatchingStartsWith(TabCompletionTypeHelper.getBooleans(), arguments[2]);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@@ -13,13 +13,15 @@ import java.util.List;
public class CommandLoadPublic extends CommandLoad implements CommandExecutor { public class CommandLoadPublic extends CommandLoad implements CommandExecutor {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
return loadBook(sender, args, "public", true); @NotNull String[] arguments) {
return loadBook(sender, arguments, "public", true);
} }
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) { public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
return doTabCompletion(sender, args, true); @NotNull String[] arguments) {
return doTabCompletion(sender, arguments, true);
} }
} }

View File

@@ -16,18 +16,24 @@ import java.util.List;
public class CommandReload implements TabExecutor { public class CommandReload implements TabExecutor {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] arguments) {
if (BooksWithoutBordersConfig.loadConfig()) { if (BooksWithoutBordersConfig.loadConfig()) {
BooksWithoutBorders.sendSuccessMessage(sender, "BooksWithoutBorders configuration reloaded!"); BooksWithoutBorders.sendSuccessMessage(sender, "BooksWithoutBorders configuration reloaded!");
} else { } else {
BooksWithoutBorders.sendErrorMessage(sender, "Reload Failed!"); BooksWithoutBorders.sendErrorMessage(sender, "Reload Failed!");
BooksWithoutBorders.sendErrorMessage(sender, "See console for details"); BooksWithoutBorders.sendErrorMessage(sender, "See console for details");
} }
// Reload books
BooksWithoutBorders.updateBooks(sender, true);
BooksWithoutBorders.clearBookData();
return true; return true;
} }
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
@NotNull String[] arguments) {
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@@ -8,6 +8,7 @@ import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
import net.knarcraft.bookswithoutborders.utility.BookHelper; import net.knarcraft.bookswithoutborders.utility.BookHelper;
import net.knarcraft.bookswithoutborders.utility.BookToFromTextHelper; import net.knarcraft.bookswithoutborders.utility.BookToFromTextHelper;
import net.knarcraft.bookswithoutborders.utility.InventoryHelper; import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor; import org.bukkit.command.TabExecutor;
@@ -32,28 +33,28 @@ import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig
public class CommandSave implements TabExecutor { public class CommandSave implements TabExecutor {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] arguments) {
return saveHeldBook(sender, args, false); return saveHeldBook(sender, arguments, false);
} }
/** /**
* Saves the player's held book if it exists * Saves the player's held book if it exists
* *
* @param sender <p>The sender of the command</p> * @param sender <p>The sender of the command</p>
* @param args <p>The arguments given</p> * @param arguments <p>The arguments given</p>
* @param savePublic <p>Whether to save the book in the public directory or the player directory</p> * @param savePublic <p>Whether to save the book in the public directory or the player directory</p>
* @return <p>True if a book was saved successfully</p> * @return <p>True if a book was saved successfully</p>
*/ */
boolean saveHeldBook(CommandSender sender, String[] args, boolean savePublic) { boolean saveHeldBook(@NotNull CommandSender sender, @NotNull String[] arguments, boolean savePublic) {
if (!(sender instanceof Player player)) { if (!(sender instanceof Player player)) {
BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!"); BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!");
return false; return false;
} }
ItemSlot holdingSlot = InventoryHelper.getHeldSlotBook(player, false, false, false, false); ItemSlot holdingSlot = InventoryHelper.getHeldSlotBook(player, false, false, false, false);
if (holdingSlot != null && holdingSlot != ItemSlot.NONE) { if (holdingSlot != ItemSlot.NONE) {
ItemStack holdingItem = InventoryHelper.getHeldItem(player, holdingSlot == ItemSlot.MAIN_HAND); ItemStack holdingItem = InventoryHelper.getHeldItem(player, holdingSlot == ItemSlot.MAIN_HAND);
boolean duplicate = args.length == 1 && Boolean.parseBoolean(args[0]); boolean duplicate = arguments.length == 1 && Boolean.parseBoolean(arguments[0]);
saveBook(player, holdingItem, duplicate, savePublic); saveBook(player, holdingItem, duplicate, savePublic);
return true; return true;
} else { } else {
@@ -70,7 +71,7 @@ public class CommandSave implements TabExecutor {
* @param overwrite <p>Whether to overwrite any existing books</p> * @param overwrite <p>Whether to overwrite any existing books</p>
* @param saveToPublicFolder <p>Whether to save the book to the public folder instead of the player folder</p> * @param saveToPublicFolder <p>Whether to save the book to the public folder instead of the player folder</p>
*/ */
public void saveBook(Player player, ItemStack heldBook, boolean overwrite, boolean saveToPublicFolder) { public void saveBook(@NotNull Player player, @NotNull ItemStack heldBook, boolean overwrite, boolean saveToPublicFolder) {
BookMeta book = (BookMeta) heldBook.getItemMeta(); BookMeta book = (BookMeta) heldBook.getItemMeta();
if (book == null) { if (book == null) {
BooksWithoutBorders.sendErrorMessage(player, "Unable to get metadata for your held book!"); BooksWithoutBorders.sendErrorMessage(player, "Unable to get metadata for your held book!");
@@ -87,8 +88,19 @@ public class CommandSave implements TabExecutor {
String savePath = BookHelper.getBookDirectoryPathString( String savePath = BookHelper.getBookDirectoryPathString(
saveToPublicFolder ? BookDirectory.PUBLIC : BookDirectory.PLAYER, player); saveToPublicFolder ? BookDirectory.PUBLIC : BookDirectory.PLAYER, player);
if (savePath == null) {
BooksWithoutBorders.sendErrorMessage(player, "Saving Failed! Unable to find the save path!");
return;
}
//Generate book filename //Generate book filename
String fileName = BookHelper.getBookFile(book, player, saveToPublicFolder); String fileName;
try {
fileName = BookHelper.getBookFile(book, player, saveToPublicFolder);
} catch (IllegalArgumentException exception) {
BooksWithoutBorders.sendErrorMessage(player, exception.getMessage());
return;
}
//Make sure the used folders exist //Make sure the used folders exist
File file = new File(savePath); File file = new File(savePath);
@@ -142,7 +154,7 @@ public class CommandSave implements TabExecutor {
//Update the relevant book list //Update the relevant book list
BooksWithoutBorders.updateBooks(player, saveToPublicFolder); BooksWithoutBorders.updateBooks(player, saveToPublicFolder);
BooksWithoutBorders.sendSuccessMessage(player, "Book Saved as \"" + fileName + "\""); BooksWithoutBorders.sendSuccessMessage(player, "Book Saved as \"" + fileName + ChatColor.RESET + "\"");
} catch (IOException exception) { } catch (IOException exception) {
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Unable to save book"); BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Unable to save book");
} }
@@ -150,8 +162,9 @@ public class CommandSave implements TabExecutor {
} }
@Override @Override
@NotNull
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
@NotNull String[] args) { @NotNull String[] arguments) {
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@@ -11,8 +11,9 @@ import org.jetbrains.annotations.NotNull;
public class CommandSavePublic extends CommandSave implements TabExecutor { public class CommandSavePublic extends CommandSave implements TabExecutor {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
return saveHeldBook(sender, args, true); @NotNull String[] arguments) {
return saveHeldBook(sender, arguments, true);
} }
} }

View File

@@ -12,6 +12,7 @@ import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.meta.BookMeta;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -22,13 +23,14 @@ import java.util.List;
public class CommandSetAuthor implements TabExecutor { public class CommandSetAuthor implements TabExecutor {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] arguments) {
if (!(sender instanceof Player player)) { if (!(sender instanceof Player player)) {
BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!"); BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!");
return false; return false;
} }
if (args.length < 1) { if (arguments.length < 1) {
BooksWithoutBorders.sendErrorMessage(player, "Too few command arguments!"); BooksWithoutBorders.sendErrorMessage(player, "Too few command arguments!");
return false; return false;
} }
@@ -43,8 +45,12 @@ public class CommandSetAuthor implements TabExecutor {
boolean mainHand = heldBookSlot == ItemSlot.MAIN_HAND; boolean mainHand = heldBookSlot == ItemSlot.MAIN_HAND;
ItemStack heldBook = InventoryHelper.getHeldItem(player, mainHand); ItemStack heldBook = InventoryHelper.getHeldItem(player, mainHand);
BookMeta bookMetaData = InventoryHelper.getHeldBookMetadata(player, mainHand); BookMeta bookMetaData = InventoryHelper.getHeldBookMetadata(player, mainHand);
if (bookMetaData == null) {
BooksWithoutBorders.sendErrorMessage(player, "Unable to get metadata for the held book!");
return false;
}
String author = ColorHelper.translateColorCodes(String.join(" ", args), ColorConversion.RGB); String author = ColorHelper.translateColorCodes(String.join(" ", arguments), ColorConversion.RGB);
bookMetaData.setAuthor(author); bookMetaData.setAuthor(author);
heldBook.setItemMeta(bookMetaData); heldBook.setItemMeta(bookMetaData);
BooksWithoutBorders.sendSuccessMessage(player, "Book author set to " + author + "!"); BooksWithoutBorders.sendSuccessMessage(player, "Book author set to " + author + "!");
@@ -52,8 +58,10 @@ public class CommandSetAuthor implements TabExecutor {
} }
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) { @Nullable
if (args.length == 1) { public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
@NotNull String[] arguments) {
if (arguments.length == 1) {
return null; return null;
} else { } else {
return new ArrayList<>(); return new ArrayList<>();

View File

@@ -26,35 +26,36 @@ public class CommandSetBookPrice implements TabExecutor {
private List<String> paymentTypes; private List<String> paymentTypes;
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] arguments) {
//Clear the current price //Clear the current price
if (args.length == 0) { if (arguments.length == 0) {
clearItemPrice(sender); clearItemPrice(sender);
return true; return true;
} }
//Warn about missing arguments //Warn about missing arguments
if (args.length < 2) { if (arguments.length < 2) {
BooksWithoutBorders.sendErrorMessage(sender, "[Item/Eco] and [quantity] must be specified!"); BooksWithoutBorders.sendErrorMessage(sender, "[Item/Eco] and [quantity] must be specified!");
return false; return false;
} }
//Warn about invalid argument //Warn about invalid argument
if (!args[0].equalsIgnoreCase("Item") && !args[0].equalsIgnoreCase("Eco")) { if (!arguments[0].equalsIgnoreCase("Item") && !arguments[0].equalsIgnoreCase("Eco")) {
BooksWithoutBorders.sendErrorMessage(sender, "Price type must be \"Item\" or \"Eco\"!"); BooksWithoutBorders.sendErrorMessage(sender, "Price type must be \"Item\" or \"Eco\"!");
return false; return false;
} }
try { try {
double price = Double.parseDouble(args[1]); double price = Double.parseDouble(arguments[1]);
if (price <= 0) { if (price <= 0) {
BooksWithoutBorders.sendErrorMessage(sender, "[quantity] must be greater than 0!"); BooksWithoutBorders.sendErrorMessage(sender, "[quantity] must be greater than 0!");
return false; return false;
} }
if (args[0].equalsIgnoreCase("Item")) { if (arguments[0].equalsIgnoreCase("Item")) {
return setItemPrice(sender, price); return setItemPrice(sender, price);
} else if (args[0].equalsIgnoreCase("Eco")) { } else if (arguments[0].equalsIgnoreCase("Eco")) {
return setEconomyPrice(sender, price); return setEconomyPrice(sender, price);
} }
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
@@ -68,7 +69,7 @@ public class CommandSetBookPrice implements TabExecutor {
* *
* @param sender <p>The sender of the command</p> * @param sender <p>The sender of the command</p>
*/ */
private void clearItemPrice(CommandSender sender) { private void clearItemPrice(@NotNull CommandSender sender) {
BooksWithoutBordersConfig.setBookPriceType(null); BooksWithoutBordersConfig.setBookPriceType(null);
BooksWithoutBordersConfig.setBookPriceQuantity(0); BooksWithoutBordersConfig.setBookPriceQuantity(0);
booksWithoutBorders.getConfig().set("Options.Price_to_create_book.Item_type", "Item type name"); booksWithoutBorders.getConfig().set("Options.Price_to_create_book.Item_type", "Item type name");
@@ -85,7 +86,7 @@ public class CommandSetBookPrice implements TabExecutor {
* @param price <p>The new price</p> * @param price <p>The new price</p>
* @return <p>True if the price was changed successfully</p> * @return <p>True if the price was changed successfully</p>
*/ */
private boolean setItemPrice(CommandSender sender, double price) { private boolean setItemPrice(@NotNull CommandSender sender, double price) {
if (!(sender instanceof Player player)) { if (!(sender instanceof Player player)) {
BooksWithoutBorders.sendErrorMessage(sender, "[Item] price can only be used by a player!"); BooksWithoutBorders.sendErrorMessage(sender, "[Item] price can only be used by a player!");
return false; return false;
@@ -117,7 +118,7 @@ public class CommandSetBookPrice implements TabExecutor {
* @param price <p>The new price</p> * @param price <p>The new price</p>
* @return <p>True if the price was changed successfully</p> * @return <p>True if the price was changed successfully</p>
*/ */
private boolean setEconomyPrice(CommandSender sender, double price) { private boolean setEconomyPrice(@NotNull CommandSender sender, double price) {
if (EconomyHelper.setupEconomy()) { if (EconomyHelper.setupEconomy()) {
BooksWithoutBordersConfig.setBookPriceQuantity(price); BooksWithoutBordersConfig.setBookPriceQuantity(price);
BooksWithoutBordersConfig.setBookPriceType(Material.AIR); BooksWithoutBordersConfig.setBookPriceType(Material.AIR);
@@ -136,16 +137,17 @@ public class CommandSetBookPrice implements TabExecutor {
} }
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) { public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
@NotNull String[] arguments) {
if (paymentTypes == null) { if (paymentTypes == null) {
initializeTabCompleteLists(); initializeTabCompleteLists();
} }
int argumentCount = args.length; int argumentCount = arguments.length;
if (argumentCount == 1) { if (argumentCount == 1) {
return TabCompletionHelper.filterMatchingStartsWith(paymentTypes, args[0]); return TabCompletionHelper.filterMatchingStartsWith(paymentTypes, arguments[0]);
} else if (argumentCount == 2) { } else if (argumentCount == 2) {
return TabCompletionHelper.filterMatchingStartsWith(TabCompletionTypeHelper.getNumbers(1, 3), args[1]); return TabCompletionHelper.filterMatchingStartsWith(TabCompletionTypeHelper.getNumbers(1, 3), arguments[1]);
} }
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@@ -0,0 +1,111 @@
package net.knarcraft.bookswithoutborders.command;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
import net.knarcraft.bookswithoutborders.container.Bookshelf;
import net.knarcraft.bookswithoutborders.handler.BookshelfHandler;
import net.knarcraft.knarlib.util.TabCompletionHelper;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* The command for setting information for a chiseled bookshelf
*/
public class CommandSetBookshelfData implements TabExecutor {
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] arguments) {
if (!(commandSender instanceof Player player)) {
BooksWithoutBorders.sendErrorMessage(commandSender, "This command must be used by a player!");
return false;
}
Block targetBlock = player.getTargetBlockExact(7);
if (targetBlock == null || targetBlock.getType() != Material.CHISELED_BOOKSHELF) {
BooksWithoutBorders.sendErrorMessage(commandSender, "You are not looking at a bookshelf!");
return false;
}
BookshelfHandler shelfHandler = BooksWithoutBorders.getBookshelfHandler();
Bookshelf bookshelf = shelfHandler.getFromLocation(targetBlock.getLocation());
if (arguments.length < 2) {
if (arguments.length == 1 && arguments[0].equalsIgnoreCase("delete")) {
if (bookshelf != null) {
shelfHandler.unregisterBookshelf(bookshelf);
shelfHandler.save();
BooksWithoutBorders.sendSuccessMessage(commandSender, "Bookshelf successfully deleted");
} else {
BooksWithoutBorders.sendErrorMessage(commandSender, "The block you are looking at is not a registered bookshelf");
}
return true;
} else {
return false;
}
}
// Get all arguments as a space-separated string
StringBuilder builder = new StringBuilder(arguments[1]);
for (int i = 2; i < arguments.length; i++) {
builder.append(" ").append(arguments[i]);
}
switch (arguments[0].toLowerCase()) {
case "name":
if (bookshelf == null) {
Bookshelf newShelf = new Bookshelf(targetBlock.getLocation(), arguments[1], new ArrayList<>());
shelfHandler.registerBookshelf(newShelf);
} else {
bookshelf.setTitle(builder.toString());
}
shelfHandler.save();
BooksWithoutBorders.sendSuccessMessage(commandSender, "Title successfully saved");
return true;
case "lore":
if (bookshelf == null) {
BooksWithoutBorders.sendErrorMessage(commandSender, "You must name the bookshelf before " +
"assigning lore!");
} else {
List<String> loreParts = Arrays.asList(builder.toString().split(BooksWithoutBordersConfig.getLoreSeparator()));
bookshelf.setLore(loreParts);
shelfHandler.save();
BooksWithoutBorders.sendSuccessMessage(commandSender, "Lore successfully saved");
}
return true;
}
return false;
}
@Nullable
@Override
public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] arguments) {
if (arguments.length == 1) {
return TabCompletionHelper.filterMatchingStartsWith(List.of("delete", "name", "lore"), arguments[0]);
} else if (arguments.length == 2) {
return switch (arguments[0].toLowerCase()) {
case "delete" -> new ArrayList<>();
case "name" ->
TabCompletionHelper.filterMatchingStartsWith(List.of("Epic Title", "Lame Title"), arguments[1]);
case "lore" ->
TabCompletionHelper.filterMatchingStartsWith(List.of("Interesting lore", "Line1~Line2~Line3"), arguments[1]);
default -> null;
};
} else {
return List.of();
}
}
}

View File

@@ -21,7 +21,7 @@ public class CommandSetGeneration implements TabExecutor {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] args) { @NotNull String[] arguments) {
if (!(sender instanceof Player player)) { if (!(sender instanceof Player player)) {
BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!"); BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!");
return false; return false;
@@ -32,14 +32,14 @@ public class CommandSetGeneration implements TabExecutor {
return false; return false;
} }
if (args.length < 1) { if (arguments.length < 1) {
BooksWithoutBorders.sendErrorMessage(player, "You must specify the new generation for your book!"); BooksWithoutBorders.sendErrorMessage(player, "You must specify the new generation for your book!");
return false; return false;
} }
BookMeta.Generation generation; BookMeta.Generation generation;
try { try {
generation = BookMeta.Generation.valueOf(args[0]); generation = BookMeta.Generation.valueOf(arguments[0]);
} catch (IllegalArgumentException exception) { } catch (IllegalArgumentException exception) {
BooksWithoutBorders.sendErrorMessage(player, "Invalid book generation specified!"); BooksWithoutBorders.sendErrorMessage(player, "Invalid book generation specified!");
return false; return false;
@@ -60,8 +60,8 @@ public class CommandSetGeneration implements TabExecutor {
@Nullable @Nullable
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] args) { @NotNull String[] arguments) {
if (args.length == 1) { if (arguments.length == 1) {
List<String> generations = new ArrayList<>(); List<String> generations = new ArrayList<>();
for (BookMeta.Generation generation : BookMeta.Generation.values()) { for (BookMeta.Generation generation : BookMeta.Generation.values()) {
generations.add(generation.name()); generations.add(generation.name());

View File

@@ -24,13 +24,14 @@ import java.util.List;
public class CommandSetLore implements TabExecutor { public class CommandSetLore implements TabExecutor {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] arguments) {
if (!(sender instanceof Player player)) { if (!(sender instanceof Player player)) {
BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!"); BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!");
return false; return false;
} }
if (args.length < 1) { if (arguments.length < 1) {
BooksWithoutBorders.sendErrorMessage(player, "Missing a command argument!"); BooksWithoutBorders.sendErrorMessage(player, "Missing a command argument!");
return false; return false;
} }
@@ -42,12 +43,11 @@ public class CommandSetLore implements TabExecutor {
} }
//Treat all arguments as lore input //Treat all arguments as lore input
String rawLore = String.join(" ", args); String rawLore = String.join(" ", arguments);
//Format lore //Format lore
rawLore = ColorHelper.translateColorCodes(rawLore, ColorConversion.RGB); rawLore = ColorHelper.translateColorCodes(rawLore, ColorConversion.RGB);
String[] loreParts = rawLore.split(BooksWithoutBordersConfig.getLoreSeparator()); List<String> newLore = Arrays.asList(rawLore.split(BooksWithoutBordersConfig.getLoreSeparator()));
List<String> newLore = new ArrayList<>(Arrays.asList(loreParts));
//Update lore //Update lore
ItemMeta meta = heldItem.getItemMeta(); ItemMeta meta = heldItem.getItemMeta();
@@ -62,7 +62,8 @@ public class CommandSetLore implements TabExecutor {
} }
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) { public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
@NotNull String[] arguments) {
//TODO: Figure out if there is a better way to display that an argument is required //TODO: Figure out if there is a better way to display that an argument is required
List<String> options = new ArrayList<>(); List<String> options = new ArrayList<>();
options.add("<new lore>"); options.add("<new lore>");

View File

@@ -24,13 +24,13 @@ public class CommandSetTitle implements TabExecutor {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] args) { @NotNull String[] arguments) {
if (!(sender instanceof Player player)) { if (!(sender instanceof Player player)) {
BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!"); BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!");
return false; return false;
} }
if (args.length < 1) { if (arguments.length < 1) {
BooksWithoutBorders.sendErrorMessage(player, "Too few command arguments!"); BooksWithoutBorders.sendErrorMessage(player, "Too few command arguments!");
return false; return false;
} }
@@ -41,7 +41,7 @@ public class CommandSetTitle implements TabExecutor {
return false; return false;
} }
String title = String.join(" ", args); String title = String.join(" ", arguments);
title = ColorHelper.translateColorCodes(title, ColorConversion.RGB); title = ColorHelper.translateColorCodes(title, ColorConversion.RGB);
ItemMeta itemMetadata = heldItem.getItemMeta(); ItemMeta itemMetadata = heldItem.getItemMeta();
@@ -69,7 +69,7 @@ public class CommandSetTitle implements TabExecutor {
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
@NotNull String[] args) { @NotNull String[] arguments) {
List<String> options = new ArrayList<>(); List<String> options = new ArrayList<>();
options.add("<new title>"); options.add("<new title>");
return options; return options;

View File

@@ -26,7 +26,8 @@ import java.util.logging.Level;
public class CommandUnSign implements TabExecutor { public class CommandUnSign implements TabExecutor {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] arguments) {
if (!(sender instanceof Player player)) { if (!(sender instanceof Player player)) {
BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!"); BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!");
return false; return false;
@@ -49,9 +50,13 @@ public class CommandUnSign implements TabExecutor {
* @param player <p>The player holding the book</p> * @param player <p>The player holding the book</p>
* @param mainHand <p>Whether the player is holding the book in its main hand or its off hand</p> * @param mainHand <p>Whether the player is holding the book in its main hand or its off hand</p>
*/ */
public void unSignHeldBook(Player player, boolean mainHand) { public void unSignHeldBook(@NotNull Player player, boolean mainHand) {
//Get the old book //Get the old book
BookMeta oldMetadata = InventoryHelper.getHeldBookMetadata(player, mainHand); BookMeta oldMetadata = InventoryHelper.getHeldBookMetadata(player, mainHand);
if (oldMetadata == null) {
BooksWithoutBorders.sendErrorMessage(player, "Unable to get metadata from the held book!");
return;
}
ItemStack heldBook = InventoryHelper.getHeldBook(player, mainHand); ItemStack heldBook = InventoryHelper.getHeldBook(player, mainHand);
//Only allow the owner to un-sign the book //Only allow the owner to un-sign the book
@@ -85,7 +90,9 @@ public class CommandUnSign implements TabExecutor {
} }
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { @NotNull
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
@NotNull String[] arguments) {
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@@ -6,6 +6,7 @@ import net.md_5.bungee.api.ChatColor;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.configuration.Configuration; import org.bukkit.configuration.Configuration;
import org.jetbrains.annotations.NotNull;
import java.nio.file.FileSystems; import java.nio.file.FileSystems;
import java.util.ArrayList; import java.util.ArrayList;
@@ -48,7 +49,7 @@ public class BooksWithoutBordersConfig {
* *
* @param booksWithoutBorders <p>The books without borders object used for getting required data</p> * @param booksWithoutBorders <p>The books without borders object used for getting required data</p>
*/ */
public static void initialize(BooksWithoutBorders booksWithoutBorders) { public static void initialize(@NotNull BooksWithoutBorders booksWithoutBorders) {
if (isInitialized) { if (isInitialized) {
throw new IllegalArgumentException("Settings class initialized twice. This should not happen!"); throw new IllegalArgumentException("Settings class initialized twice. This should not happen!");
} }
@@ -388,7 +389,7 @@ public class BooksWithoutBordersConfig {
* @param configOption <p>The configuration option to get the value for</p> * @param configOption <p>The configuration option to get the value for</p>
* @return <p>The value of the option</p> * @return <p>The value of the option</p>
*/ */
private static String getString(Configuration config, ConfigOption configOption) { private static String getString(@NotNull Configuration config, @NotNull ConfigOption configOption) {
return config.getString(configOption.getConfigNode(), (String) configOption.getDefaultValue()); return config.getString(configOption.getConfigNode(), (String) configOption.getDefaultValue());
} }
@@ -399,7 +400,7 @@ public class BooksWithoutBordersConfig {
* @param configOption <p>The configuration option to get the value for</p> * @param configOption <p>The configuration option to get the value for</p>
* @return <p>The value of the option</p> * @return <p>The value of the option</p>
*/ */
private static boolean getBoolean(Configuration config, ConfigOption configOption) { private static boolean getBoolean(@NotNull Configuration config, @NotNull ConfigOption configOption) {
return config.getBoolean(configOption.getConfigNode(), (Boolean) configOption.getDefaultValue()); return config.getBoolean(configOption.getConfigNode(), (Boolean) configOption.getDefaultValue());
} }

View File

@@ -1,5 +1,7 @@
package net.knarcraft.bookswithoutborders.config; package net.knarcraft.bookswithoutborders.config;
import org.jetbrains.annotations.NotNull;
/** /**
* A representation of the different available config options * A representation of the different available config options
*/ */
@@ -90,7 +92,7 @@ public enum ConfigOption {
* @param configNode <p>The config node in the config file this option represents</p> * @param configNode <p>The config node in the config file this option represents</p>
* @param defaultValue <p>The default value for this config option</p> * @param defaultValue <p>The default value for this config option</p>
*/ */
ConfigOption(String configNode, Object defaultValue) { ConfigOption(@NotNull String configNode, @NotNull Object defaultValue) {
this.configNode = configNode; this.configNode = configNode;
this.defaultValue = defaultValue; this.defaultValue = defaultValue;
} }

View File

@@ -0,0 +1,81 @@
package net.knarcraft.bookswithoutborders.container;
import org.bukkit.Location;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* A representation of a bookshelf with extra data used when displaying its contents
*/
public class Bookshelf {
private final @NotNull Location location;
private @NotNull String title;
private @NotNull List<String> lore;
/**
* Instantiates a new bookshelf
*
* @param location <p>The location of the bookshelf</p>
* @param title <p>The title of the bookshelf</p>
* @param lore <p>The lore of the bookshelf</p>
*/
public Bookshelf(@NotNull Location location, @NotNull String title, @NotNull List<String> lore) {
this.location = location;
this.title = title;
this.lore = lore;
}
/**
* Gets the location of this bookshelf
*
* @return <p>The location of this bookshelf</p>
*/
@NotNull
public Location getLocation() {
return this.location;
}
/**
* Gets the title of this bookshelf
*
* @return <p>The title of this bookshelf</p>
*/
@NotNull
public String getTitle() {
return this.title;
}
/**
* Gets the lore of this bookshelf
*
* @return <p>The lore of this bookshelf</p>
*/
@NotNull
public List<String> getLore() {
return this.lore;
}
/**
* Sets the title of this bookshelf
*
* @param title <p>The new title</p>
*/
public void setTitle(@NotNull String title) {
if (title.isBlank()) {
throw new IllegalArgumentException("Bookshelves cannot have empty titles!");
}
this.title = title;
}
/**
* Sets the lore of this bookshelf
*
* @param lore <p>The new lore</p>
*/
public void setLore(@NotNull List<String> lore) {
this.lore = lore;
}
}

View File

@@ -1,6 +1,8 @@
package net.knarcraft.bookswithoutborders.encryption; package net.knarcraft.bookswithoutborders.encryption;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders; import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.crypto.BadPaddingException; import javax.crypto.BadPaddingException;
import javax.crypto.Cipher; import javax.crypto.Cipher;
@@ -52,7 +54,8 @@ public class AES {
* @param encrypt <p>Whether to encrypt or decrypt the input</p> * @param encrypt <p>Whether to encrypt or decrypt the input</p>
* @return <p>The encrypted/decrypted input, or null if anything went wrong</p> * @return <p>The encrypted/decrypted input, or null if anything went wrong</p>
*/ */
public String encryptDecryptText(String input, String password, boolean encrypt) { @Nullable
public String encryptDecryptText(@NotNull String input, @NotNull String password, boolean encrypt) {
//Make a key from the password //Make a key from the password
SecretKeySpec secretKeySpec = getKeyFromPassword(password); SecretKeySpec secretKeySpec = getKeyFromPassword(password);
//Get cipher instance //Get cipher instance
@@ -102,7 +105,7 @@ public class AES {
* @param encryption <p>Whether the input should be encrypted or decrypted</p> * @param encryption <p>Whether the input should be encrypted or decrypted</p>
* @return <p>The input in byte format</p> * @return <p>The input in byte format</p>
*/ */
private byte[] getInputBytes(String input, boolean encryption) { private byte[] getInputBytes(@NotNull String input, boolean encryption) {
if (encryption) { if (encryption) {
return input.getBytes(); return input.getBytes();
} else { } else {
@@ -117,6 +120,7 @@ public class AES {
* @param encryption <p>Whether the output came from encryption or decryption</p> * @param encryption <p>Whether the output came from encryption or decryption</p>
* @return <p>The output as a string</p> * @return <p>The output as a string</p>
*/ */
@NotNull
private String createResult(byte[] output, boolean encryption) { private String createResult(byte[] output, boolean encryption) {
if (encryption) { if (encryption) {
return Base64.getEncoder().encodeToString(output); return Base64.getEncoder().encodeToString(output);
@@ -130,6 +134,7 @@ public class AES {
* *
* @return <p>An AES cipher instance, or null if something went wrong</p> * @return <p>An AES cipher instance, or null if something went wrong</p>
*/ */
@Nullable
private Cipher getAESCipher() { private Cipher getAESCipher() {
Cipher aes; Cipher aes;
try { try {
@@ -147,7 +152,8 @@ public class AES {
* @param password <p>A user supplied password</p> * @param password <p>A user supplied password</p>
* @return <p>A secret key spec or null if something went wrong</p> * @return <p>A secret key spec or null if something went wrong</p>
*/ */
private SecretKeySpec getKeyFromPassword(String password) { @Nullable
private SecretKeySpec getKeyFromPassword(@NotNull String password) {
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), this.passwordSalt, 1000, 128); PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), this.passwordSalt, 1000, 128);
SecretKeyFactory keyFactory; SecretKeyFactory keyFactory;
try { try {

View File

@@ -1,5 +1,7 @@
package net.knarcraft.bookswithoutborders.encryption; package net.knarcraft.bookswithoutborders.encryption;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Random; import java.util.Random;
@@ -24,7 +26,7 @@ public class GenenCrypt {
* *
* @param key <p>The key used to generate the codon table</p> * @param key <p>The key used to generate the codon table</p>
*/ */
public GenenCrypt(String key) { public GenenCrypt(@NotNull String key) {
// define the initial, unshuffled codon list of 4 base codons // define the initial, unshuffled codon list of 4 base codons
ArrayList<String> originalCodonList = new ArrayList<>(); ArrayList<String> originalCodonList = new ArrayList<>();
@@ -114,7 +116,8 @@ public class GenenCrypt {
* @param input <p>The input to encrypt</p> * @param input <p>The input to encrypt</p>
* @return <p>The encrypted input</p> * @return <p>The encrypted input</p>
*/ */
public String encrypt(String input) { @NotNull
public String encrypt(@NotNull String input) {
StringBuilder output = new StringBuilder(); StringBuilder output = new StringBuilder();
for (int i = 0; i < input.length(); i++) { for (int i = 0; i < input.length(); i++) {
// insert junk bases // insert junk bases
@@ -147,7 +150,8 @@ public class GenenCrypt {
* @param input <p>The input to decrypt</p> * @param input <p>The input to decrypt</p>
* @return <p>The decrypted input</p> * @return <p>The decrypted input</p>
*/ */
public String decrypt(String input) { @NotNull
public String decrypt(@NotNull String input) {
StringBuilder output = new StringBuilder(); StringBuilder output = new StringBuilder();
int keyCount = 0; int keyCount = 0;
int junk = key.charAt(0); int junk = key.charAt(0);

View File

@@ -1,5 +1,7 @@
package net.knarcraft.bookswithoutborders.encryption; package net.knarcraft.bookswithoutborders.encryption;
import org.jetbrains.annotations.NotNull;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.StringTokenizer; import java.util.StringTokenizer;
@@ -17,9 +19,10 @@ public class SubstitutionCipher {
// using a string for the key, it is converted // using a string for the key, it is converted
// a series of offsets that each character in the // a series of offsets that each character in the
// original message is offset by // original message is offset by
public String encrypt(String in, String key) { @NotNull
public String encrypt(@NotNull String in, @NotNull String key) {
StringBuilder output = new StringBuilder(); StringBuilder output = new StringBuilder();
if (key != null && !key.isEmpty()) { if (!key.isEmpty()) {
StringTokenizer tokenizer = new StringTokenizer(key, ", "); // tokenizes the key StringTokenizer tokenizer = new StringTokenizer(key, ", "); // tokenizes the key
// converts each number in the key to an integer and adds to an array // converts each number in the key to an integer and adds to an array
int[] offsetArray = new int[tokenizer.countTokens()]; int[] offsetArray = new int[tokenizer.countTokens()];
@@ -53,9 +56,10 @@ public class SubstitutionCipher {
// method with a flag for encryption / decryption, but // method with a flag for encryption / decryption, but
// I'm lazy. // I'm lazy.
@SuppressWarnings("unused") @SuppressWarnings("unused")
public String decrypt(String in, String key) { @NotNull
public String decrypt(@NotNull String in, @NotNull String key) {
StringBuilder output = new StringBuilder(); StringBuilder output = new StringBuilder();
if (key != null && !key.isEmpty()) { if (!key.isEmpty()) {
StringTokenizer tokenizer = new StringTokenizer(key, ", "); // tokenizes the key StringTokenizer tokenizer = new StringTokenizer(key, ", "); // tokenizes the key
// converts each number in the key to an integer and adds to an array // converts each number in the key to an integer and adds to an array
int[] offsetArray = new int[tokenizer.countTokens()]; int[] offsetArray = new int[tokenizer.countTokens()];
@@ -81,7 +85,8 @@ public class SubstitutionCipher {
// encryption works just like decryption, but is // encryption works just like decryption, but is
// vulnerable if the same key is used more than once. // vulnerable if the same key is used more than once.
@SuppressWarnings("unused") @SuppressWarnings("unused")
public String oneTimePad(String in, String key) { @NotNull
public String oneTimePad(@NotNull String in, @NotNull String key) {
StringBuilder output = new StringBuilder(); StringBuilder output = new StringBuilder();
for (int i = 0; i < in.length(); i++) { for (int i = 0; i < in.length(); i++) {
output.append((char) (in.charAt(i) ^ key.charAt(i % key.length()))); output.append((char) (in.charAt(i) ^ key.charAt(i % key.length())));

View File

@@ -0,0 +1,87 @@
package net.knarcraft.bookswithoutborders.gui;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.utility.BookFormatter;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.hover.content.Text;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class AuthorBookIndex extends BookIndex {
private final static int booksPerPage = 10;
/**
* Prints the available books
*
* @param sender <p>The sender to display the books to</p>
* @param listPublic <p>Whether to display public books</p>
* @param command <p>The base command causing this to be called</p>
* @param page <p>The page of the book list to display</p>
*/
public static void printBooks(@NotNull CommandSender sender, boolean listPublic, @NotNull String command, int page,
@NotNull String authorName) {
List<String> availableBooks = BooksWithoutBorders.getAvailableBooks(sender, listPublic);
availableBooks.removeIf((bookPath) -> !BookFormatter.stripColor(bookPath.substring(0, bookPath.length() - 4).split(",")[1]).equalsIgnoreCase(authorName));
int totalPages = (int) Math.ceil((double) availableBooks.size() / booksPerPage);
if (page > totalPages) {
sender.sendMessage(ChatColor.GRAY + "No such page");
} else {
showAuthorBooks(sender, command, page, totalPages, availableBooks, authorName);
}
}
/**
* Shows a menu listing available books from an author
*
* @param sender <p>The sender wanting to see the book menu</p>
* @param command <p>The main command used to trigger display of the book menu</p>
* @param page <p>The currently selected page</p>
* @param totalPages <p>The total amount of pages</p>
* @param availableBooks <p>All books available to the sender</p>
* @param authorName <p>The name of the author currently shown</p>
*/
private static void showAuthorBooks(@NotNull CommandSender sender, @NotNull String command, int page,
int totalPages, @NotNull List<String> availableBooks, @NotNull String authorName) {
ComponentBuilder componentBuilder = new ComponentBuilder();
// Display the list of books, with the next and previous buttons
displayPreviousButton(componentBuilder, command + " author" + authorName, page);
componentBuilder.append("\n");
displayBookList(componentBuilder, command, page, availableBooks);
displayNextButton(componentBuilder, command + " author" + authorName, page, totalPages);
// Display total pages and the manual change page command suggestion
componentBuilder.append(" ", ComponentBuilder.FormatRetention.NONE);
displayTotalPages(componentBuilder, page, totalPages);
componentBuilder.append(" ", ComponentBuilder.FormatRetention.NONE);
sender.spigot().sendMessage(componentBuilder.create());
}
/**
* Displays the list of books on the current page
*
* @param componentBuilder <p>The component builder to append to</p>
* @param command <p>The command used for switching pages</p>
* @param page <p>The current page</p>
* @param availableBooks <p>All available books</p>
*/
protected static void displayBookList(@NotNull ComponentBuilder componentBuilder, @NotNull String command, int page,
@NotNull List<String> availableBooks) {
int startIndex = (page - 1) * booksPerPage;
for (int bookIndex = startIndex; bookIndex < Math.min(startIndex + booksPerPage, availableBooks.size()); bookIndex++) {
componentBuilder.append(getNiceName(availableBooks.get(bookIndex))).color(ChatColor.WHITE).event(
new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + command + " " +
availableBooks.get(bookIndex))).event(
new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Select book by path")));
componentBuilder.append("\n");
}
}
}

View File

@@ -0,0 +1,163 @@
package net.knarcraft.bookswithoutborders.gui;
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.hover.content.Text;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
public abstract class BookIndex {
protected final static int booksPerPage = 10;
protected final static @NotNull ChatColor interactColor = ChatColor.of("#FFD700");
protected final static @NotNull ChatColor inactiveColor = ChatColor.of("#999999");
/**
* Displays the correct GUI, if specified in the given arguments
*
* @param arguments <p>The arguments given by a command sender</p>
* @param sender <p>The sender executing the command</p>
* @param selectPublic <p>Whether to display public books, or only those available to the command sender</p>
* @param command <p>The command used for changing pages and making the final selection</p>
* @return <p>True if the GUI was displayed</p>
*/
public static boolean displayPage(@NotNull String[] arguments, @NotNull CommandSender sender, boolean selectPublic,
@NotNull String command) {
if (arguments.length == 0) {
PagedBookIndex.printBooks(sender, selectPublic, command, 1);
return true;
} else if (arguments.length == 1) {
int page = InputCleaningHelper.parsePageNumber(arguments[0]);
if (page > 0) {
PagedBookIndex.printBooks(sender, selectPublic, command, page);
return true;
}
} else if (arguments.length == 2) {
String author = InputCleaningHelper.parseAuthorSpecifier(arguments[0]);
if (author != null) {
int page = InputCleaningHelper.parsePageNumber(arguments[1]);
if (page > 0) {
AuthorBookIndex.printBooks(sender, selectPublic, command, page, author);
}
return true;
}
}
return false;
}
/**
* Displays the suggestion for manually going to any page
*
* @param componentBuilder <p>The component builder to append to</p>
* @param command <p>The command used for switching pages</p>
* @param page <p>The current page</p>
*/
protected static void displayPageCommand(@NotNull ComponentBuilder componentBuilder, @NotNull String command, int page) {
componentBuilder.append("[Page Command]", ComponentBuilder.FormatRetention.NONE).color(interactColor).event(new HoverEvent(
HoverEvent.Action.SHOW_TEXT, new Text("/" + command + " page" + page))).event(
new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + command + " page" + page));
}
/**
* Displays the current page and total amount of pages
*
* @param componentBuilder <p>The component builder to append to</p>
* @param page <p>The current page</p>
* @param totalPages <p>The total amount of pages</p>
*/
protected static void displayTotalPages(@NotNull ComponentBuilder componentBuilder, int page, int totalPages) {
componentBuilder.append("Page " + page + " of " + totalPages,
ComponentBuilder.FormatRetention.NONE).color(inactiveColor);
}
/**
* Displays the alphabet-based page index
*
* @param componentBuilder <p>The component builder to append to</p>
* @param command <p>The command used for switching pages</p>
* @param firstInstances <p>The map of where the first index of a letter is found</p>
*/
protected static void displayAlphabetIndex(@NotNull ComponentBuilder componentBuilder,
@NotNull String command, @NotNull Map<Character, Integer> firstInstances) {
componentBuilder.append("[index] <", ComponentBuilder.FormatRetention.NONE).color(inactiveColor);
for (int characterIndex = 0; characterIndex <= 25; characterIndex++) {
char character = (char) ('a' + characterIndex);
if (firstInstances.containsKey(character)) {
int pageIndex = (firstInstances.get(character) / booksPerPage) + 1;
componentBuilder.append(character + "").color(interactColor).event(
new ClickEvent(ClickEvent.Action.RUN_COMMAND,
"/" + command + " page" + pageIndex)).event(new HoverEvent(
HoverEvent.Action.SHOW_TEXT, new Text("Books starting with " + character)));
} else {
componentBuilder.append(character + "", ComponentBuilder.FormatRetention.NONE).color(inactiveColor);
}
}
componentBuilder.append(">", ComponentBuilder.FormatRetention.NONE).color(inactiveColor);
}
/**
* Displays the previous page button
*
* @param componentBuilder <p>The component builder to append to</p>
* @param command <p>The command used for switching pages</p>
* @param page <p>The current page</p>
*/
protected static void displayPreviousButton(@NotNull ComponentBuilder componentBuilder,
@NotNull String command, int page) {
if (page > 1) {
String fullCommand = "/" + command + " page" + (page - 1);
HoverEvent prevPagePreview = new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("To page " + (page - 1)));
ClickEvent prevPageClick = new ClickEvent(ClickEvent.Action.RUN_COMMAND, fullCommand);
componentBuilder.append("Previous [<]", ComponentBuilder.FormatRetention.NONE).color(interactColor)
.event(prevPagePreview).event(prevPageClick);
} else {
componentBuilder.append("Previous [<]", ComponentBuilder.FormatRetention.NONE).color(inactiveColor);
}
}
/**
* Displays the next page button
*
* @param componentBuilder <p>The component builder to append to</p>
* @param command <p>The command used for switching pages</p>
* @param page <p>The current page</p>
* @param totalPages <p>The total amount of pages</p>
*/
protected static void displayNextButton(@NotNull ComponentBuilder componentBuilder,
@NotNull String command, int page, int totalPages) {
if (page < totalPages) {
String fullCommand = "/" + command + " page" + (page + 1);
HoverEvent nextPagePreview = new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("To page " + (page + 1)));
ClickEvent nextPageClick = new ClickEvent(ClickEvent.Action.RUN_COMMAND, fullCommand);
componentBuilder.append("Next [>]", ComponentBuilder.FormatRetention.NONE).color(interactColor)
.event(nextPagePreview).event(nextPageClick);
} else {
componentBuilder.append("Next [>]", ComponentBuilder.FormatRetention.NONE).color(inactiveColor);
}
}
/**
* Gets a nice name from a book's path
*
* @param bookPath <p>The path of a book</p>
* @return <p>The prettified book name</p>
*/
@NotNull
protected static String getNiceName(@NotNull String bookPath) {
String separator = BooksWithoutBordersConfig.getTitleAuthorSeparator();
bookPath = ChatColor.translateAlternateColorCodes('&', bookPath.substring(0, bookPath.length() - 4));
if (bookPath.contains(separator)) {
String[] parts = bookPath.split(separator);
return parts[0].replace("_", " ") + ChatColor.RESET + " by " + parts[1] + ChatColor.RESET;
} else {
return bookPath + ChatColor.RESET + " by Unknown" + ChatColor.RESET;
}
}
}

View File

@@ -0,0 +1,118 @@
package net.knarcraft.bookswithoutborders.gui;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.utility.BookFormatter;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.hover.content.Text;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A class for displaying a paged index of all available books
*/
public class PagedBookIndex extends BookIndex {
/**
* Prints the available books
*
* @param sender <p>The sender to display the books to</p>
* @param listPublic <p>Whether to display public books</p>
* @param command <p>The base command causing this to be called</p>
* @param page <p>The page of the book list to display</p>
*/
public static void printBooks(@NotNull CommandSender sender, boolean listPublic, @NotNull String command, int page) {
List<String> availableBooks = BooksWithoutBorders.getAvailableBooks(sender, listPublic);
Map<Character, Integer> firstInstances;
if (listPublic) {
firstInstances = BooksWithoutBorders.getLetterIndex(null);
} else if (sender instanceof Player player) {
firstInstances = BooksWithoutBorders.getLetterIndex(player.getUniqueId());
} else {
firstInstances = new HashMap<>();
}
int totalPages = (int) Math.ceil((double) availableBooks.size() / booksPerPage);
if (page > totalPages) {
sender.sendMessage(ChatColor.GRAY + "No such page");
} else {
showBookMenu(sender, command, page, totalPages, availableBooks, firstInstances);
}
}
/**
* Shows a menu listing available books
*
* @param sender <p>The sender wanting to see the book menu</p>
* @param command <p>The main command used to trigger display of the book menu</p>
* @param page <p>The currently selected page</p>
* @param totalPages <p>The total amount of pages</p>
* @param availableBooks <p>All books available to the sender</p>
* @param firstInstances <p>The map between a character, and the index of the first instance of that character in the book list</p>
*/
private static void showBookMenu(@NotNull CommandSender sender, @NotNull String command, int page,
int totalPages, @NotNull List<String> availableBooks,
@NotNull Map<Character, Integer> firstInstances) {
ComponentBuilder componentBuilder = new ComponentBuilder();
// Display the list of books, with the next and previous buttons
displayPreviousButton(componentBuilder, command, page);
componentBuilder.append("\n");
displayBookList(componentBuilder, command, page, availableBooks);
displayNextButton(componentBuilder, command, page, totalPages);
// Display total pages and the manual change page command suggestion
componentBuilder.append(" ", ComponentBuilder.FormatRetention.NONE);
displayTotalPages(componentBuilder, page, totalPages);
componentBuilder.append(" ", ComponentBuilder.FormatRetention.NONE);
displayPageCommand(componentBuilder, command, page);
componentBuilder.append("\n");
// Display the alphabet index as the header
displayAlphabetIndex(componentBuilder, command, firstInstances);
sender.spigot().sendMessage(componentBuilder.create());
}
/**
* Displays the list of books on the current page
*
* @param componentBuilder <p>The component builder to append to</p>
* @param command <p>The command used for switching pages</p>
* @param page <p>The current page</p>
* @param availableBooks <p>All available books</p>
*/
protected static void displayBookList(@NotNull ComponentBuilder componentBuilder, @NotNull String command, int page,
@NotNull List<String> availableBooks) {
int startIndex = (page - 1) * booksPerPage;
for (int bookIndex = startIndex; bookIndex < Math.min(startIndex + booksPerPage, availableBooks.size()); bookIndex++) {
componentBuilder.append("[" + (bookIndex + 1) + "]").color(interactColor).event(
new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + command + " " +
(bookIndex + 1))).event(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
new Text("Select book by index")));
componentBuilder.append(" ", ComponentBuilder.FormatRetention.NONE);
String[] parts = getNiceName(availableBooks.get(bookIndex)).split(" by ");
componentBuilder.append(parts[0]).color(ChatColor.WHITE).event(
new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + command + " " +
availableBooks.get(bookIndex))).event(
new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Select book by path")));
componentBuilder.append(" by ", ComponentBuilder.FormatRetention.NONE).color(ChatColor.WHITE);
componentBuilder.append(parts[1]).color(ChatColor.WHITE).event(
new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/" + command + " author" +
BookFormatter.stripColor(parts[1]) + " page1")).event(
new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Books by " +
BookFormatter.stripColor(parts[1]))));
componentBuilder.append("\n");
}
}
}

View File

@@ -0,0 +1,145 @@
package net.knarcraft.bookswithoutborders.handler;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.container.Bookshelf;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
/**
* A handler keeping track of all bookshelves
*/
public class BookshelfHandler {
private static final File bookshelfFile = new File(BooksWithoutBorders.getInstance().getDataFolder(),
"bookshelves.yml");
private Set<Bookshelf> bookshelves;
private Map<Location, Bookshelf> locationLookup;
/**
* Gets a bookshelf from the given location
*
* @param location <p>The location of the bookshelf</p>
* @return <p>The bookshelf at the location, or null if no such bookshelf exists</p>
*/
@Nullable
public Bookshelf getFromLocation(@NotNull Location location) {
return locationLookup.get(location);
}
/**
* Registers the given bookshelf to this handler
*
* @param bookshelf <p>The bookshelf to register</p>
*/
public void registerBookshelf(@NotNull Bookshelf bookshelf) {
this.bookshelves.add(bookshelf);
this.locationLookup.put(bookshelf.getLocation(), bookshelf);
}
/**
* Unregisters the given bookshelf from this handler
*
* @param bookshelf <p>The bookshelf to unregister</p>
*/
public void unregisterBookshelf(@NotNull Bookshelf bookshelf) {
this.locationLookup.remove(bookshelf.getLocation());
this.bookshelves.remove(bookshelf);
}
/**
* Loads all stored bookshelves
*/
public void load() {
this.bookshelves = new HashSet<>();
this.locationLookup = new HashMap<>();
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(bookshelfFile);
ConfigurationSection bookshelfSection = configuration.getConfigurationSection("bookshelves");
if (bookshelfSection == null) {
BooksWithoutBorders.getInstance().getLogger().log(Level.INFO,
"BooksWithoutBorders found no bookshelves to load");
return;
}
for (String key : bookshelfSection.getKeys(false)) {
String[] locationInfo = key.split(",");
World world = Bukkit.getWorld(UUID.fromString(locationInfo[0]));
double x = Integer.parseInt(locationInfo[1]);
double y = Integer.parseInt(locationInfo[2]);
double z = Integer.parseInt(locationInfo[3]);
Location bookshelfLocation = new Location(world, x, y, z);
String titleKey = key + ".title";
String loreKey = key + ".lore";
String title = bookshelfSection.getString(titleKey, null);
List<String> loreStrings = new ArrayList<>();
List<?> lore = bookshelfSection.getList(loreKey);
if (lore == null) {
throw new IllegalArgumentException("Lore is missing from bookshelf data!");
}
lore.forEach((item) -> {
if (item instanceof String) {
loreStrings.add((String) item);
}
});
if (title != null) {
registerBookshelf(new Bookshelf(bookshelfLocation, title, loreStrings));
}
}
}
/**
* Saves all current bookshelves
*/
public void save() {
try {
YamlConfiguration configuration = new YamlConfiguration();
ConfigurationSection bookshelfSection = configuration.createSection("bookshelves");
for (Bookshelf bookshelf : bookshelves) {
saveBookshelf(bookshelfSection, bookshelf);
}
configuration.save(bookshelfFile);
} catch (IOException exception) {
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Unable to save bookshelves!");
}
}
/**
* Saves a bookshelf to the given configuration section
*
* @param section <p>The configuration section to save to</p>
* @param bookshelf <p>The bookshelf to save</p>
*/
private void saveBookshelf(@NotNull ConfigurationSection section, @NotNull Bookshelf bookshelf) {
Location location = bookshelf.getLocation();
if (location.getWorld() == null) {
return;
}
String key = location.getWorld().getUID() + "," + location.getBlockX() + "," + location.getBlockY() +
"," + location.getBlockZ();
String titleKey = key + ".title";
String loreKey = key + ".lore";
section.set(titleKey, bookshelf.getTitle());
section.set(loreKey, bookshelf.getLore());
}
}

View File

@@ -5,6 +5,7 @@ import net.knarcraft.bookswithoutborders.utility.BookFormatter;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerEditBookEvent; import org.bukkit.event.player.PlayerEditBookEvent;
import org.jetbrains.annotations.NotNull;
/** /**
* A listener for listening to book events * A listener for listening to book events
@@ -14,7 +15,7 @@ import org.bukkit.event.player.PlayerEditBookEvent;
public class BookEventListener implements Listener { public class BookEventListener implements Listener {
@EventHandler @EventHandler
public void onBookSign(PlayerEditBookEvent event) { public void onBookSign(@NotNull PlayerEditBookEvent event) {
if (event.isCancelled() || !event.isSigning() || !BooksWithoutBordersConfig.formatBooks()) { if (event.isCancelled() || !event.isSigning() || !BooksWithoutBordersConfig.formatBooks()) {
return; return;
} }

View File

@@ -1,35 +1,58 @@
package net.knarcraft.bookswithoutborders.listener; package net.knarcraft.bookswithoutborders.listener;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig; import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
import net.knarcraft.bookswithoutborders.container.Bookshelf;
import net.knarcraft.bookswithoutborders.handler.BookshelfHandler;
import net.knarcraft.bookswithoutborders.utility.IntegerToRomanConverter; import net.knarcraft.bookswithoutborders.utility.IntegerToRomanConverter;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.ChiseledBookshelf; import org.bukkit.block.ChiseledBookshelf;
import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.block.Action; import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ChiseledBookshelfInventory; import org.bukkit.inventory.ChiseledBookshelfInventory;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.meta.BookMeta;
import org.bukkit.inventory.meta.EnchantmentStorageMeta; import org.bukkit.inventory.meta.EnchantmentStorageMeta;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getSuccessColor;
/** /**
* A listener for bookshelf clicking * A listener for bookshelf clicking
*/ */
public class BookshelfListener implements Listener { public class BookshelfListener implements Listener {
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onBookshelfBreak(@NotNull BlockBreakEvent event) {
Block block = event.getBlock();
if (block.getType() != Material.CHISELED_BOOKSHELF) {
return;
}
BookshelfHandler bookshelfHandler = BooksWithoutBorders.getBookshelfHandler();
Bookshelf bookshelf = bookshelfHandler.getFromLocation(block.getLocation());
if (bookshelf != null) {
bookshelfHandler.unregisterBookshelf(bookshelf);
bookshelfHandler.save();
}
}
@EventHandler @EventHandler
public void onBookshelfClick(PlayerInteractEvent event) { public void onBookshelfClick(@NotNull PlayerInteractEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
// If left-clicking a chiseled bookshelf and sneaking, display contents // If left-clicking a chiseled bookshelf and sneaking, display contents
@@ -49,29 +72,53 @@ public class BookshelfListener implements Listener {
event.setUseItemInHand(Event.Result.DENY); event.setUseItemInHand(Event.Result.DENY);
ChiseledBookshelfInventory bookshelfInventory = chiseledBookshelf.getInventory(); ChiseledBookshelfInventory bookshelfInventory = chiseledBookshelf.getInventory();
player.sendMessage(getBookshelfDescription(bookshelfInventory)); player.sendMessage(getBookshelfDescription(bookshelfInventory, event.getClickedBlock().getLocation()));
} }
/** /**
* Gets the description for a bookshelf's contents * Gets the description for a bookshelf's contents
* *
* @param bookshelfInventory <p>The inventory of the bookshelf to describe</p> * @param bookshelfInventory <p>The inventory of the bookshelf to describe</p>
* @param location <p>The location of the clicked bookshelf</p>
* @return <p>A textual description of the bookshelf's contents</p> * @return <p>A textual description of the bookshelf's contents</p>
*/ */
private String getBookshelfDescription(ChiseledBookshelfInventory bookshelfInventory) { @NotNull
StringBuilder builder = new StringBuilder(getSuccessColor() + "Books in shelf:"); private String getBookshelfDescription(@NotNull ChiseledBookshelfInventory bookshelfInventory, @NotNull Location location) {
for (ItemStack itemStack : bookshelfInventory.getStorageContents()) { StringBuilder builder = new StringBuilder();
Bookshelf bookshelf = BooksWithoutBorders.getBookshelfHandler().getFromLocation(location);
if (bookshelf != null) {
builder.append(ChatColor.of("#FF5700")).append("Books in ").append(bookshelf.getTitle())
.append(":").append(ChatColor.RESET);
for (String lore : bookshelf.getLore()) {
builder.append("\n ").append(ChatColor.LIGHT_PURPLE).append(lore);
}
} else {
builder.append(ChatColor.of("#FF5700")).append("Books in shelf:").append(ChatColor.RESET);
}
for (int i = 0; i < bookshelfInventory.getSize(); i++) {
int index = (i % 3) + 1;
if (i % 3 == 0) {
builder.append("\n ").append(ChatColor.of("#FF5700")).append(
i < 3 ? "Top Row:" : "Bottom Row:").append(ChatColor.RESET);
}
builder.append("\n ").append(ChatColor.of("#ffd700")).append(index).append(". ").append(ChatColor.RESET);
ItemStack itemStack = bookshelfInventory.getItem(i);
if (itemStack == null) { if (itemStack == null) {
builder.append(ChatColor.GRAY).append("<empty>");
continue; continue;
} }
ItemMeta meta = itemStack.getItemMeta(); ItemMeta meta = itemStack.getItemMeta();
builder.append("\n ").append(ChatColor.GRAY).append(" - ");
if (meta instanceof BookMeta bookMeta) { if (meta instanceof BookMeta bookMeta) {
builder.append(getBookDescription(bookMeta)); builder.append(getBookDescription(bookMeta));
} else if (meta instanceof EnchantmentStorageMeta enchantmentStorageMeta) { } else if (meta instanceof EnchantmentStorageMeta enchantmentStorageMeta) {
builder.append(getEnchantedBookDescription(enchantmentStorageMeta)); builder.append(getEnchantedBookDescription(enchantmentStorageMeta));
} else if (meta != null) { } else if (meta != null) {
builder.append(getPlainBookDescription(meta)); builder.append(ChatColor.of("#A5682A")).append("[P]").append(ChatColor.RESET).append(getPlainBookDescription(meta));
} }
} }
return builder.toString(); return builder.toString();
@@ -83,7 +130,8 @@ public class BookshelfListener implements Listener {
* @param itemMeta <p>The metadata for the book to describe</p> * @param itemMeta <p>The metadata for the book to describe</p>
* @return <p>The description of the book</p> * @return <p>The description of the book</p>
*/ */
private String getPlainBookDescription(ItemMeta itemMeta) { @NotNull
private String getPlainBookDescription(@NotNull ItemMeta itemMeta) {
String name = itemMeta.getDisplayName(); String name = itemMeta.getDisplayName();
if (name.isEmpty()) { if (name.isEmpty()) {
name = "Plain book"; name = "Plain book";
@@ -97,7 +145,8 @@ public class BookshelfListener implements Listener {
* @param bookMeta <p>The metadata for the book to describe</p> * @param bookMeta <p>The metadata for the book to describe</p>
* @return <p>The book's description</p> * @return <p>The book's description</p>
*/ */
private String getBookDescription(BookMeta bookMeta) { @NotNull
private String getBookDescription(@NotNull BookMeta bookMeta) {
String title; String title;
String author; String author;
if (!bookMeta.hasTitle() || bookMeta.getTitle() == null) { if (!bookMeta.hasTitle() || bookMeta.getTitle() == null) {
@@ -110,7 +159,7 @@ public class BookshelfListener implements Listener {
} else { } else {
author = bookMeta.getAuthor(); author = bookMeta.getAuthor();
} }
return title + " by " + author; return ChatColor.of("#686868") + "[Q]" + ChatColor.RESET + title + ChatColor.RESET + " by " + author;
} }
/** /**
@@ -119,9 +168,10 @@ public class BookshelfListener implements Listener {
* @param enchantmentStorageMeta <p>The metadata for the enchanted book to describe</p> * @param enchantmentStorageMeta <p>The metadata for the enchanted book to describe</p>
* @return <p>The enchanted book's description</p> * @return <p>The enchanted book's description</p>
*/ */
private String getEnchantedBookDescription(EnchantmentStorageMeta enchantmentStorageMeta) { @NotNull
private String getEnchantedBookDescription(@NotNull EnchantmentStorageMeta enchantmentStorageMeta) {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append("Enchanted book ("); builder.append(ChatColor.of("#A64CFF")).append("[E]").append(ChatColor.RESET);
Map<Enchantment, Integer> enchantmentMap = enchantmentStorageMeta.getStoredEnchants(); Map<Enchantment, Integer> enchantmentMap = enchantmentStorageMeta.getStoredEnchants();
List<String> enchantments = new ArrayList<>(enchantmentMap.size()); List<String> enchantments = new ArrayList<>(enchantmentMap.size());
for (Map.Entry<Enchantment, Integer> enchantmentEntry : enchantmentMap.entrySet()) { for (Map.Entry<Enchantment, Integer> enchantmentEntry : enchantmentMap.entrySet()) {
@@ -129,7 +179,6 @@ public class BookshelfListener implements Listener {
IntegerToRomanConverter.getRomanNumber(enchantmentEntry.getValue())); IntegerToRomanConverter.getRomanNumber(enchantmentEntry.getValue()));
} }
builder.append(String.join(", ", enchantments)); builder.append(String.join(", ", enchantments));
builder.append(")");
return builder.toString(); return builder.toString();
} }
@@ -139,7 +188,8 @@ public class BookshelfListener implements Listener {
* @param enchantment <p>The enchantment to get the name of</p> * @param enchantment <p>The enchantment to get the name of</p>
* @return <p>The prettified enchantment name</p> * @return <p>The prettified enchantment name</p>
*/ */
private String getEnchantmentName(Enchantment enchantment) { @NotNull
private String getEnchantmentName(@NotNull Enchantment enchantment) {
// Note: While depreciated, changing this is incompatible with Paper // Note: While depreciated, changing this is incompatible with Paper
return uppercaseFirst(enchantment.getKey().getKey().replace("_", " ")); return uppercaseFirst(enchantment.getKey().getKey().replace("_", " "));
} }
@@ -150,7 +200,8 @@ public class BookshelfListener implements Listener {
* @param input <p>The input to uppercase</p> * @param input <p>The input to uppercase</p>
* @return <p>The input string with more uppercase</p> * @return <p>The input string with more uppercase</p>
*/ */
private String uppercaseFirst(String input) { @NotNull
private String uppercaseFirst(@NotNull String input) {
String[] parts = input.split(" "); String[] parts = input.split(" ");
for (int i = 0; i < parts.length; i++) { for (int i = 0; i < parts.length; i++) {
parts[i] = parts[i].substring(0, 1).toUpperCase() + parts[i].substring(1); parts[i] = parts[i].substring(0, 1).toUpperCase() + parts[i].substring(1);

View File

@@ -17,6 +17,8 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory; import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.meta.BookMeta;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
import java.util.logging.Level; import java.util.logging.Level;
@@ -29,7 +31,7 @@ public class PlayerEventListener implements Listener {
private final BooksWithoutBorders booksWithoutBorders = BooksWithoutBorders.getInstance(); private final BooksWithoutBorders booksWithoutBorders = BooksWithoutBorders.getInstance();
@EventHandler @EventHandler
public void onHold(PlayerItemHeldEvent event) { public void onHold(@NotNull PlayerItemHeldEvent event) {
if (event.isCancelled()) { if (event.isCancelled()) {
return; return;
} }
@@ -54,7 +56,7 @@ public class PlayerEventListener implements Listener {
} }
@EventHandler @EventHandler
public void onPlayerJoin(PlayerJoinEvent event) { public void onPlayerJoin(@NotNull PlayerJoinEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
//If a book directory exists with this player's name, move it to this player's UUID //If a book directory exists with this player's name, move it to this player's UUID
@@ -82,13 +84,17 @@ public class PlayerEventListener implements Listener {
ItemStack offHandItem = InventoryHelper.getHeldItem(player, false); ItemStack offHandItem = InventoryHelper.getHeldItem(player, false);
if (mainHandItem.getType() == Material.WRITTEN_BOOK) { if (mainHandItem.getType() == Material.WRITTEN_BOOK) {
ItemMeta itemMetadata = mainHandItem.getItemMeta(); ItemMeta itemMetadata = mainHandItem.getItemMeta();
if (itemMetadata != null) {
updateBookInHand(player, itemMetadata, true); updateBookInHand(player, itemMetadata, true);
} }
}
if (offHandItem.getType() == Material.WRITTEN_BOOK) { if (offHandItem.getType() == Material.WRITTEN_BOOK) {
ItemMeta itemMetadata = offHandItem.getItemMeta(); ItemMeta itemMetadata = offHandItem.getItemMeta();
if (itemMetadata != null) {
updateBookInHand(player, itemMetadata, false); updateBookInHand(player, itemMetadata, false);
} }
} }
}
/** /**
* Gives a book to a player joining for the first time * Gives a book to a player joining for the first time
@@ -98,7 +104,7 @@ public class PlayerEventListener implements Listener {
* @param sendMessage <p>Whether to send a message to the joining player</p> * @param sendMessage <p>Whether to send a message to the joining player</p>
* @return <p>True if a message has yet to be sent</p> * @return <p>True if a message has yet to be sent</p>
*/ */
private boolean giveBookToNewPlayer(String bookName, Player player, boolean sendMessage) { private boolean giveBookToNewPlayer(@NotNull String bookName, @NotNull Player player, boolean sendMessage) {
if (!bookName.trim().isEmpty()) { if (!bookName.trim().isEmpty()) {
//Give the book to the player if it exists //Give the book to the player if it exists
@@ -125,7 +131,7 @@ public class PlayerEventListener implements Listener {
* @param itemMetadata <p>Information about the held book</p> * @param itemMetadata <p>Information about the held book</p>
* @param mainHand <p>Whether to update the book in the player's main hand</p> * @param mainHand <p>Whether to update the book in the player's main hand</p>
*/ */
private void updateBookInHand(Player player, ItemMeta itemMetadata, boolean mainHand) { private void updateBookInHand(@NotNull Player player, @NotNull ItemMeta itemMetadata, boolean mainHand) {
PlayerInventory playerInventory = player.getInventory(); PlayerInventory playerInventory = player.getInventory();
ItemStack updatedBook = updateBook(player, (BookMeta) itemMetadata); ItemStack updatedBook = updateBook(player, (BookMeta) itemMetadata);
if (updatedBook != null) { if (updatedBook != null) {
@@ -144,7 +150,8 @@ public class PlayerEventListener implements Listener {
* @param oldBook <p>Metadata about the held book</p> * @param oldBook <p>Metadata about the held book</p>
* @return <p>An updated book</p> * @return <p>An updated book</p>
*/ */
public ItemStack updateBook(Player player, BookMeta oldBook) { @Nullable
public ItemStack updateBook(@NotNull Player player, @NotNull BookMeta oldBook) {
//handles hacked title-less books //handles hacked title-less books
if (oldBook.getTitle() == null || oldBook.getTitle().length() < 3) { if (oldBook.getTitle() == null || oldBook.getTitle().length() < 3) {
return null; return null;

View File

@@ -4,6 +4,7 @@ import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig; import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
import net.knarcraft.bookswithoutborders.state.EncryptionStyle; import net.knarcraft.bookswithoutborders.state.EncryptionStyle;
import net.knarcraft.bookswithoutborders.utility.BookFileHelper; import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
import net.knarcraft.bookswithoutborders.utility.BookFormatter;
import net.knarcraft.bookswithoutborders.utility.BookLoader; import net.knarcraft.bookswithoutborders.utility.BookLoader;
import net.knarcraft.bookswithoutborders.utility.EncryptionHelper; import net.knarcraft.bookswithoutborders.utility.EncryptionHelper;
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper; import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper;
@@ -24,6 +25,8 @@ import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory; import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.meta.BookMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
@@ -39,7 +42,7 @@ public class SignEventListener implements Listener {
private final String slash = getSlash(); private final String slash = getSlash();
@EventHandler @EventHandler
public void onSignChange(SignChangeEvent event) { public void onSignChange(@NotNull SignChangeEvent event) {
if (event.isCancelled()) { if (event.isCancelled()) {
return; return;
} }
@@ -79,7 +82,7 @@ public class SignEventListener implements Listener {
} }
@EventHandler @EventHandler
public void onClick(PlayerInteractEvent event) { public void onClick(@NotNull PlayerInteractEvent event) {
if (event.getClickedBlock() == null) { if (event.getClickedBlock() == null) {
return; return;
} }
@@ -138,13 +141,14 @@ public class SignEventListener implements Listener {
* @param player <p>The player which clicked the sign</p> * @param player <p>The player which clicked the sign</p>
* @param hand <p>The EquipmentSlot of the used hand</p> * @param hand <p>The EquipmentSlot of the used hand</p>
*/ */
private void decryptHeldBookUsingSign(Sign sign, Material heldItemType, Player player, EquipmentSlot hand) { private void decryptHeldBookUsingSign(@NotNull Sign sign, @NotNull Material heldItemType, @NotNull Player player,
@NotNull EquipmentSlot hand) {
//Decrypt the held book and replace it //Decrypt the held book and replace it
if (heldItemType == Material.WRITTEN_BOOK) { if (heldItemType == Material.WRITTEN_BOOK) {
player.closeInventory(); player.closeInventory();
//Converts user supplied key into integer form //Converts user supplied key into integer form
String lineText = ChatColor.stripColor(sign.getSide(Side.FRONT).getLine(2)); String lineText = BookFormatter.stripColor(sign.getSide(Side.FRONT).getLine(2));
String key = EncryptionHelper.getNumberKeyFromStringKey(lineText); String key = EncryptionHelper.getNumberKeyFromStringKey(lineText);
ItemStack book = EncryptionHelper.loadEncryptedBook(player, key, false); ItemStack book = EncryptionHelper.loadEncryptedBook(player, key, false);
@@ -161,9 +165,10 @@ public class SignEventListener implements Listener {
* @param sign <p>The sign to check</p> * @param sign <p>The sign to check</p>
* @return <p>The color of the sign</p> * @return <p>The color of the sign</p>
*/ */
private ChatColor getSignLine2Color(Sign sign) { @Nullable
private ChatColor getSignLine2Color(@NotNull Sign sign) {
String line = sign.getSide(Side.FRONT).getLine(2); String line = sign.getSide(Side.FRONT).getLine(2);
if (!ChatColor.stripColor(line).equals(line)) { if (!BookFormatter.stripColor(line).equals(line)) {
return ChatColor.getByChar(sign.getSide(Side.FRONT).getLine(2).substring(1, 2).charAt(0)); return ChatColor.getByChar(sign.getSide(Side.FRONT).getLine(2).substring(1, 2).charAt(0));
} else { } else {
return null; return null;
@@ -179,7 +184,8 @@ public class SignEventListener implements Listener {
* @param color <p>The color to match</p> * @param color <p>The color to match</p>
* @return <p>True if the given string is what's on the sign</p> * @return <p>True if the given string is what's on the sign</p>
*/ */
private boolean signLineEquals(Sign sign, int lineNumber, String compareTo, ChatColor color) { private boolean signLineEquals(@NotNull Sign sign, int lineNumber, @NotNull String compareTo,
@NotNull ChatColor color) {
String line = sign.getSide(Side.FRONT).getLine(lineNumber); String line = sign.getSide(Side.FRONT).getLine(lineNumber);
return line.equalsIgnoreCase(color + compareTo); return line.equalsIgnoreCase(color + compareTo);
} }
@@ -191,7 +197,8 @@ public class SignEventListener implements Listener {
* @param lines <p>The lines on the sign</p> * @param lines <p>The lines on the sign</p>
* @param player <p>The player which edited the sign</p> * @param player <p>The player which edited the sign</p>
*/ */
private void generateGiveSign(SignChangeEvent event, String[] lines, Player player) { private void generateGiveSign(@NotNull SignChangeEvent event, @NotNull String[] lines,
@NotNull Player player) {
if (lines[2].length() > 13 || lines[3].length() > 13) { if (lines[2].length() > 13 || lines[3].length() > 13) {
BooksWithoutBorders.sendErrorMessage(player, BooksWithoutBorders.sendErrorMessage(player,
"[Give] signs' 3rd and 4th lines must be 13 characters or less!"); "[Give] signs' 3rd and 4th lines must be 13 characters or less!");
@@ -220,7 +227,7 @@ public class SignEventListener implements Listener {
* @param event <p>The event causing the creation of the give sign</p> * @param event <p>The event causing the creation of the give sign</p>
* @param isValid <p>Whether the created sign is valid</p> * @param isValid <p>Whether the created sign is valid</p>
*/ */
private void markGiveSignValidity(SignChangeEvent event, boolean isValid) { private void markGiveSignValidity(@NotNull SignChangeEvent event, boolean isValid) {
String[] lines = event.getLines(); String[] lines = event.getLines();
if (isValid) { if (isValid) {
event.setLine(2, ChatColor.DARK_GREEN + lines[2]); event.setLine(2, ChatColor.DARK_GREEN + lines[2]);
@@ -239,7 +246,8 @@ public class SignEventListener implements Listener {
* @param heldItem <p>The type of the held book</p> * @param heldItem <p>The type of the held book</p>
* @param hand <p>The hand the player is using to hold the book</p> * @param hand <p>The hand the player is using to hold the book</p>
*/ */
private void decryptBook(BookMeta oldBook, Player player, ItemStack heldItem, EquipmentSlot hand) { private void decryptBook(@NotNull BookMeta oldBook, @NotNull Player player, @NotNull ItemStack heldItem,
@NotNull EquipmentSlot hand) {
ItemStack newBook; ItemStack newBook;
//Check if the book is encrypted by Books Without Borders //Check if the book is encrypted by Books Without Borders
@@ -285,14 +293,15 @@ public class SignEventListener implements Listener {
* @param player <p>The player which clicked the sign</p> * @param player <p>The player which clicked the sign</p>
* @param hand <p>The EquipmentSlot of the used hand</p> * @param hand <p>The EquipmentSlot of the used hand</p>
*/ */
private void encryptHeldBookUsingSign(Sign sign, Material heldItemType, Player player, EquipmentSlot hand) { private void encryptHeldBookUsingSign(@NotNull Sign sign, @NotNull Material heldItemType, @NotNull Player player,
@NotNull EquipmentSlot hand) {
ItemStack eBook; ItemStack eBook;
String[] lines = sign.getSide(Side.FRONT).getLines(); String[] lines = sign.getSide(Side.FRONT).getLines();
boolean mainHand = hand == EquipmentSlot.HAND; boolean mainHand = hand == EquipmentSlot.HAND;
if (heldItemType == Material.WRITTEN_BOOK) { if (heldItemType == Material.WRITTEN_BOOK) {
player.closeInventory(); player.closeInventory();
eBook = EncryptionHelper.encryptBook(player, mainHand, ChatColor.stripColor(lines[2]), eBook = EncryptionHelper.encryptBook(player, mainHand, BookFormatter.stripColor(lines[2]),
EncryptionStyle.getFromString(ChatColor.stripColor(lines[3]))); EncryptionStyle.getFromString(BookFormatter.stripColor(lines[3])));
if (eBook != null) { if (eBook != null) {
player.getInventory().setItem(hand, eBook); player.getInventory().setItem(hand, eBook);
} }
@@ -305,8 +314,8 @@ public class SignEventListener implements Listener {
* @param sign <p>The sign the user clicked</p> * @param sign <p>The sign the user clicked</p>
* @param player <p>The player which clicked the sign</p> * @param player <p>The player which clicked the sign</p>
*/ */
private void giveBook(Sign sign, Player player) { private void giveBook(@NotNull Sign sign, @NotNull Player player) {
String fileName = ChatColor.stripColor(sign.getSide(Side.FRONT).getLine(2)); String fileName = BookFormatter.stripColor(sign.getSide(Side.FRONT).getLine(2));
boolean isLoadListNumber = false; boolean isLoadListNumber = false;
try { try {
@@ -318,7 +327,7 @@ public class SignEventListener implements Listener {
//Add the third line to the second line for the full filename //Add the third line to the second line for the full filename
String thirdLine = sign.getSide(Side.FRONT).getLine(3); String thirdLine = sign.getSide(Side.FRONT).getLine(3);
if (!isLoadListNumber && thirdLine.length() >= 2) { if (!isLoadListNumber && thirdLine.length() >= 2) {
fileName += ChatColor.stripColor(thirdLine); fileName += BookFormatter.stripColor(thirdLine);
} }
ItemStack newBook = BookLoader.loadBook(player, fileName, "true", "public"); ItemStack newBook = BookLoader.loadBook(player, fileName, "true", "public");

View File

@@ -1,5 +1,8 @@
package net.knarcraft.bookswithoutborders.state; package net.knarcraft.bookswithoutborders.state;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** /**
* This enum represents the different directories books can be saved in * This enum represents the different directories books can be saved in
*/ */
@@ -26,7 +29,8 @@ public enum BookDirectory {
* @param directory <p>The directory to transform</p> * @param directory <p>The directory to transform</p>
* @return <p>A book directory, or null if the given directory is empty</p> * @return <p>A book directory, or null if the given directory is empty</p>
*/ */
public static BookDirectory getFromString(String directory) { @Nullable
public static BookDirectory getFromString(@NotNull String directory) {
if (directory.equalsIgnoreCase("public")) { if (directory.equalsIgnoreCase("public")) {
return BookDirectory.PUBLIC; return BookDirectory.PUBLIC;
} else if (directory.equalsIgnoreCase("player")) { } else if (directory.equalsIgnoreCase("player")) {

View File

@@ -1,5 +1,7 @@
package net.knarcraft.bookswithoutborders.state; package net.knarcraft.bookswithoutborders.state;
import org.jetbrains.annotations.NotNull;
/** /**
* This enum represents the different available encryption styles * This enum represents the different available encryption styles
*/ */
@@ -10,7 +12,7 @@ public enum EncryptionStyle {
private final String name; private final String name;
EncryptionStyle(String name) { EncryptionStyle(@NotNull String name) {
this.name = name; this.name = name;
} }
@@ -20,7 +22,8 @@ public enum EncryptionStyle {
* @param name <p>The name of the encryption style</p> * @param name <p>The name of the encryption style</p>
* @return <p>An encryption style or null if no match is found</p> * @return <p>An encryption style or null if no match is found</p>
*/ */
public static EncryptionStyle getFromString(String name) { @NotNull
public static EncryptionStyle getFromString(@NotNull String name) {
for (EncryptionStyle style : EncryptionStyle.values()) { for (EncryptionStyle style : EncryptionStyle.values()) {
if (style.name.equalsIgnoreCase(name)) { if (style.name.equalsIgnoreCase(name)) {
return style; return style;

View File

@@ -3,12 +3,16 @@ package net.knarcraft.bookswithoutborders.utility;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders; import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig; import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
import net.knarcraft.bookswithoutborders.state.BookDirectory; import net.knarcraft.bookswithoutborders.state.BookDirectory;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -28,7 +32,7 @@ public final class BookFileHelper {
* @param possibleIndex <p>The string which might be a book index</p> * @param possibleIndex <p>The string which might be a book index</p>
* @return <p>True if the number is a book index</p> * @return <p>True if the number is a book index</p>
*/ */
public static boolean isBookListIndex(String possibleIndex) { public static boolean isBookListIndex(@NotNull String possibleIndex) {
File bookDirectory = new File(getBookFolder().replaceAll("[\\\\/]$", "")); File bookDirectory = new File(getBookFolder().replaceAll("[\\\\/]$", ""));
try { try {
@@ -49,7 +53,7 @@ public final class BookFileHelper {
* @param bookFile <p>The path to a book</p> * @param bookFile <p>The path to a book</p>
* @return <p>True if the file exists and points to a book file</p> * @return <p>True if the file exists and points to a book file</p>
*/ */
public static boolean bookFileExists(String bookFile) { public static boolean bookFileExists(@NotNull String bookFile) {
return ((new File(bookFile).isFile() && (bookFile.endsWith(".txt") || return ((new File(bookFile).isFile() && (bookFile.endsWith(".txt") ||
bookFile.endsWith(".yml"))) || new File(bookFile + ".txt").isFile() || bookFile.endsWith(".yml"))) || new File(bookFile + ".txt").isFile() ||
new File(bookFile + ".yml").isFile()) && !bookFile.contains("../") && !bookFile.contains("..\\"); new File(bookFile + ".yml").isFile()) && !bookFile.contains("../") && !bookFile.contains("..\\");
@@ -63,7 +67,8 @@ public final class BookFileHelper {
* @param bookPath <p>The path of the book to get</p> * @param bookPath <p>The path of the book to get</p>
* @return <p>The file the path points to, or null otherwise</p> * @return <p>The file the path points to, or null otherwise</p>
*/ */
public static File getBookFile(String bookPath) { @Nullable
public static File getBookFile(@NotNull String bookPath) {
if (!bookFileExists(bookPath)) { if (!bookFileExists(bookPath)) {
return null; return null;
} }
@@ -93,7 +98,8 @@ public final class BookFileHelper {
* @param listPublic <p>Whether to list public or personal files</p> * @param listPublic <p>Whether to list public or personal files</p>
* @return <p>A list of available files</p> * @return <p>A list of available files</p>
*/ */
public static List<String> listFiles(CommandSender sender, Boolean listPublic) { @Nullable
public static List<String> listFiles(@NotNull CommandSender sender, @NotNull Boolean listPublic) {
File file = BookHelper.getBookDirectoryPath(listPublic ? BookDirectory.PUBLIC : BookDirectory.PLAYER, sender); File file = BookHelper.getBookDirectoryPath(listPublic ? BookDirectory.PUBLIC : BookDirectory.PLAYER, sender);
if (file == null) { if (file == null) {
return new ArrayList<>(); return new ArrayList<>();
@@ -102,31 +108,23 @@ public final class BookFileHelper {
} }
/** /**
* Prints the available books * Gets a map between characters, and the first instance of a book's title starting with that character
* *
* @param sender <p>The sender to display the books to</p> * @param books <p>The books to look through</p>
* @param listPublic <p>Whether to display public books</p> * @return <p>The map of the first index containing each character</p>
*/ */
public static void printBooks(CommandSender sender, boolean listPublic) { @NotNull
List<String> availableBooks = BooksWithoutBorders.getAvailableBooks(sender, listPublic); public static Map<Character, Integer> populateLetterIndices(@NotNull List<String> books) {
BookFileHelper.printFiles(sender, availableBooks); Map<Character, Integer> firstEncounter = new HashMap<>();
Character current = null;
for (int i = 0; i < books.size(); i++) {
char first = BookFormatter.stripColor(books.get(i)).toLowerCase().charAt(0);
if (current == null || current != first) {
current = first;
firstEncounter.put(first, i);
} }
/**
* Prints a list of files
*
* @param sender <p>The command sender to show the list to</p>
* @param fileList <p>The files to list</p>
*/
public static void printFiles(CommandSender sender, List<String> fileList) {
BooksWithoutBorders.sendSuccessMessage(sender, "Available Books:");
if (fileList == null) {
return;
}
int listSize = fileList.size();
for (int fileIndex = 0; fileIndex < listSize; fileIndex++) {
sender.sendMessage(ChatColor.GRAY + "[" + (fileIndex + 1) + "] " + fileList.get(fileIndex));
} }
return firstEncounter;
} }
/** /**
@@ -136,7 +134,8 @@ public final class BookFileHelper {
* @param searchDirectory <p>The directory to search for files</p> * @param searchDirectory <p>The directory to search for files</p>
* @return <p>A list of available files</p> * @return <p>A list of available files</p>
*/ */
private static List<String> listFiles(CommandSender sender, File searchDirectory) { @Nullable
private static List<String> listFiles(@NotNull CommandSender sender, @NotNull File searchDirectory) {
List<String> fileList = new ArrayList<>(); List<String> fileList = new ArrayList<>();
File[] existingFiles = searchDirectory.listFiles(); File[] existingFiles = searchDirectory.listFiles();
@@ -146,7 +145,8 @@ public final class BookFileHelper {
} }
for (File foundFile : existingFiles) { for (File foundFile : existingFiles) {
if (!foundFile.isFile()) { // Filter out invalid files
if (!foundFile.isFile() || foundFile.getName().contains(" ") || foundFile.getName().contains("§")) {
continue; continue;
} }
String fileName = foundFile.getName(); String fileName = foundFile.getName();
@@ -163,6 +163,10 @@ public final class BookFileHelper {
} }
} }
// Sort the book list
Comparator<String> bookComparator = Comparator.naturalOrder();
fileList.sort((a, b) -> bookComparator.compare(BookFormatter.stripColor(a).toLowerCase(),
BookFormatter.stripColor(b).toLowerCase()));
return fileList; return fileList;
} }
@@ -173,7 +177,7 @@ public final class BookFileHelper {
* @param fileName <p>The name of the file which might already exist</p> * @param fileName <p>The name of the file which might already exist</p>
* @return <p>The number of found duplicates</p> * @return <p>The number of found duplicates</p>
*/ */
public static int findDuplicates(File[] foundFiles, String fileName) { public static int findDuplicates(@NotNull File[] foundFiles, @NotNull String fileName) {
int foundDuplicates = 0; int foundDuplicates = 0;
for (File foundFile : foundFiles) { for (File foundFile : foundFiles) {
if (foundFile.getName().matches("(\\([0-9]+\\))?" + Pattern.quote(fileName) + "(\\.yml|\\.txt)?")) { if (foundFile.getName().matches("(\\([0-9]+\\))?" + Pattern.quote(fileName) + "(\\.yml|\\.txt)?")) {

View File

@@ -3,6 +3,7 @@ package net.knarcraft.bookswithoutborders.utility;
import net.knarcraft.knarlib.property.ColorConversion; import net.knarcraft.knarlib.property.ColorConversion;
import net.knarcraft.knarlib.util.ColorHelper; import net.knarcraft.knarlib.util.ColorHelper;
import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.meta.BookMeta;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -24,7 +25,7 @@ public final class BookFormatter {
* *
* @param rawPages <p>A list of pages</p> * @param rawPages <p>A list of pages</p>
*/ */
public static void formatLastPage(List<String> rawPages) { public static void formatLastPage(@NotNull List<String> rawPages) {
int maxPageText = 256; int maxPageText = 256;
int fitsNewline = maxPageText - 2; int fitsNewline = maxPageText - 2;
@@ -49,7 +50,7 @@ public final class BookFormatter {
* @param maxPageText <p>The max number of characters which fit on a page</p> * @param maxPageText <p>The max number of characters which fit on a page</p>
* @param fitsNewline <p>The max number of characters on a page which still fits a newline character</p> * @param fitsNewline <p>The max number of characters on a page which still fits a newline character</p>
*/ */
public static void formatLastPageSplitOverflow(List<String> rawPages, int maxPageText, int fitsNewline) { public static void formatLastPageSplitOverflow(@NotNull List<String> rawPages, int maxPageText, int fitsNewline) {
while (rawPages.get(rawPages.size() - 1).length() > maxPageText) { while (rawPages.get(rawPages.size() - 1).length() > maxPageText) {
int splitPosition; int splitPosition;
String fittingText = rawPages.get(rawPages.size() - 1).substring(0, maxPageText); String fittingText = rawPages.get(rawPages.size() - 1).substring(0, maxPageText);
@@ -77,7 +78,7 @@ public final class BookFormatter {
* @param rawPages <p>The raw pages to format</p> * @param rawPages <p>The raw pages to format</p>
* @param maxPageText <p>The max number of characters which fit on a page</p> * @param maxPageText <p>The max number of characters which fit on a page</p>
*/ */
public static void formatLastPageCombinePages(List<String> rawPages, int maxPageText) { public static void formatLastPageCombinePages(@NotNull List<String> rawPages, int maxPageText) {
int lastPageIndex = rawPages.size() - 1; int lastPageIndex = rawPages.size() - 1;
int nextToLastIndex = rawPages.size() - 2; int nextToLastIndex = rawPages.size() - 2;
if (rawPages.get(nextToLastIndex).length() + rawPages.get(lastPageIndex).length() <= maxPageText) { if (rawPages.get(nextToLastIndex).length() + rawPages.get(lastPageIndex).length() <= maxPageText) {
@@ -92,7 +93,7 @@ public final class BookFormatter {
* @param rawPages <p>The raw pages to format</p> * @param rawPages <p>The raw pages to format</p>
* @param fitsNewline <p>The max number of characters on a page which still fits a newline character</p> * @param fitsNewline <p>The max number of characters on a page which still fits a newline character</p>
*/ */
public static void formatLastPageAddNewline(List<String> rawPages, int fitsNewline) { public static void formatLastPageAddNewline(@NotNull List<String> rawPages, int fitsNewline) {
int pageIndex = rawPages.size() - 1; int pageIndex = rawPages.size() - 1;
if (rawPages.get(pageIndex).length() <= fitsNewline && !rawPages.get(pageIndex).isEmpty()) { if (rawPages.get(pageIndex).length() <= fitsNewline && !rawPages.get(pageIndex).isEmpty()) {
rawPages.set(pageIndex, (rawPages.get(pageIndex)) + "\n"); rawPages.set(pageIndex, (rawPages.get(pageIndex)) + "\n");
@@ -105,7 +106,8 @@ public final class BookFormatter {
* @param bookMeta <p>The book meta to change</p> * @param bookMeta <p>The book meta to change</p>
* @return <p>The changed book meta</p> * @return <p>The changed book meta</p>
*/ */
public static BookMeta formatPages(BookMeta bookMeta) { @NotNull
public static BookMeta formatPages(@NotNull BookMeta bookMeta) {
List<String> formattedPages = new ArrayList<>(Objects.requireNonNull(bookMeta).getPageCount()); List<String> formattedPages = new ArrayList<>(Objects.requireNonNull(bookMeta).getPageCount());
for (String page : bookMeta.getPages()) { for (String page : bookMeta.getPages()) {
formattedPages.add(ColorHelper.translateColorCodes(page, ColorConversion.RGB)); formattedPages.add(ColorHelper.translateColorCodes(page, ColorConversion.RGB));
@@ -114,4 +116,15 @@ public final class BookFormatter {
return bookMeta; return bookMeta;
} }
/**
* Strips the color from the given input
*
* @param input <p>The input to strip</p>
* @return <p>The color stripped input</p>
*/
@NotNull
public static String stripColor(@NotNull String input) {
return ColorHelper.stripColorCodes(input, ColorConversion.RGB);
}
} }

View File

@@ -8,6 +8,8 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.meta.BookMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
import java.util.UUID; import java.util.UUID;
@@ -31,7 +33,8 @@ public final class BookHelper {
* @param author <p>The author string</p> * @param author <p>The author string</p>
* @return <p>The author string, converted if it was a UUID</p> * @return <p>The author string, converted if it was a UUID</p>
*/ */
public static String authorFromUUID(String author) { @NotNull
public static String authorFromUUID(@NotNull String author) {
try { try {
UUID authorID = UUID.fromString(author); UUID authorID = UUID.fromString(author);
Player player = Bukkit.getPlayer(authorID); Player player = Bukkit.getPlayer(authorID);
@@ -50,7 +53,8 @@ public final class BookHelper {
* @param sender <p>The command sender trying to get the directory</p> * @param sender <p>The command sender trying to get the directory</p>
* @return <p>The path of the directory, or null if not possible to get</p> * @return <p>The path of the directory, or null if not possible to get</p>
*/ */
public static File getBookDirectoryPath(BookDirectory bookDirectory, CommandSender sender) { @Nullable
public static File getBookDirectoryPath(@NotNull BookDirectory bookDirectory, @NotNull CommandSender sender) {
String bookFolderString = getBookDirectoryPathString(bookDirectory, sender); String bookFolderString = getBookDirectoryPathString(bookDirectory, sender);
if (bookFolderString == null) { if (bookFolderString == null) {
return null; return null;
@@ -65,7 +69,8 @@ public final class BookHelper {
* @param sender <p>The command sender trying to get the directory</p> * @param sender <p>The command sender trying to get the directory</p>
* @return <p>The path of the directory, or null if not possible to get</p> * @return <p>The path of the directory, or null if not possible to get</p>
*/ */
public static String getBookDirectoryPathString(BookDirectory bookDirectory, CommandSender sender) { @Nullable
public static String getBookDirectoryPathString(@NotNull BookDirectory bookDirectory, @NotNull CommandSender sender) {
String folder = null; String folder = null;
String bookFolder = BooksWithoutBordersConfig.getBookFolder(); String bookFolder = BooksWithoutBordersConfig.getBookFolder();
if (bookDirectory == BookDirectory.PUBLIC) { if (bookDirectory == BookDirectory.PUBLIC) {
@@ -81,7 +86,7 @@ public final class BookHelper {
* *
* @param bookItem <p>The book item to increase the generation of</p> * @param bookItem <p>The book item to increase the generation of</p>
*/ */
public static void increaseGeneration(ItemStack bookItem) { public static void increaseGeneration(@NotNull ItemStack bookItem) {
BookMeta bookMeta = (BookMeta) bookItem.getItemMeta(); BookMeta bookMeta = (BookMeta) bookItem.getItemMeta();
if (BooksWithoutBordersConfig.changeGenerationOnCopy() && bookMeta != null) { if (BooksWithoutBordersConfig.changeGenerationOnCopy() && bookMeta != null) {
bookMeta.setGeneration(BookHelper.getNextGeneration(bookMeta.getGeneration())); bookMeta.setGeneration(BookHelper.getNextGeneration(bookMeta.getGeneration()));
@@ -98,7 +103,8 @@ public final class BookHelper {
* @param currentGeneration <p>The current generation of the book</p> * @param currentGeneration <p>The current generation of the book</p>
* @return <p>The next generation of the book</p> * @return <p>The next generation of the book</p>
*/ */
public static BookMeta.Generation getNextGeneration(BookMeta.Generation currentGeneration) { @NotNull
public static BookMeta.Generation getNextGeneration(@Nullable BookMeta.Generation currentGeneration) {
if (currentGeneration == null) { if (currentGeneration == null) {
return BookMeta.Generation.COPY_OF_ORIGINAL; return BookMeta.Generation.COPY_OF_ORIGINAL;
} }
@@ -115,12 +121,17 @@ public final class BookHelper {
* @param book <p>The book to get the file of</p> * @param book <p>The book to get the file of</p>
* @param player <p>The player trying to do something with the book</p> * @param player <p>The player trying to do something with the book</p>
* @return <p>The book file</p> * @return <p>The book file</p>
* @throws IllegalArgumentException <p>If the book title or author contains the title author separator</p>
*/ */
public static String getBookFile(BookMeta book, Player player, boolean isPublic) { @NotNull
public static String getBookFile(@NotNull BookMeta book, @NotNull Player player, boolean isPublic) throws IllegalArgumentException {
String titleAuthorSeparator = BooksWithoutBordersConfig.getTitleAuthorSeparator(); String titleAuthorSeparator = BooksWithoutBordersConfig.getTitleAuthorSeparator();
String bookName; String bookName;
if (book.hasTitle()) { if (book.hasTitle()) {
bookName = book.getTitle(); bookName = book.getTitle();
if (bookName == null) {
bookName = "Untitled";
}
} else { } else {
bookName = "Untitled"; bookName = "Untitled";
} }
@@ -133,6 +144,14 @@ public final class BookHelper {
authorName = player.getName(); authorName = player.getName();
} else { } else {
authorName = book.getAuthor(); authorName = book.getAuthor();
if (authorName == null) {
authorName = "Unknown";
}
}
if (bookName.contains(titleAuthorSeparator) || authorName.contains(titleAuthorSeparator)) {
throw new IllegalArgumentException("The author or title contains the title author separator. Saving this " +
"book would lead to unexpected problems.");
} }
return fixName(cleanString(bookName + titleAuthorSeparator + authorName), false); return fixName(cleanString(bookName + titleAuthorSeparator + authorName), false);
@@ -145,7 +164,7 @@ public final class BookHelper {
* @param book <p>The book to check</p> * @param book <p>The book to check</p>
* @return <p>True if the player is not the book's author</p> * @return <p>True if the player is not the book's author</p>
*/ */
public static boolean isNotAuthor(Player player, BookMeta book) { public static boolean isNotAuthor(@NotNull Player player, @NotNull BookMeta book) {
if (isAuthor(player.getName(), book.getAuthor())) { if (isAuthor(player.getName(), book.getAuthor())) {
return false; return false;
} else { } else {
@@ -162,7 +181,7 @@ public final class BookHelper {
* @param author <p>The author to check</p> * @param author <p>The author to check</p>
* @return <p>True if the player is the author</p> * @return <p>True if the player is the author</p>
*/ */
private static boolean isAuthor(String playerName, String author) { private static boolean isAuthor(@NotNull String playerName, @Nullable String author) {
playerName = InputCleaningHelper.cleanString(playerName); playerName = InputCleaningHelper.cleanString(playerName);
return author != null && playerName.equalsIgnoreCase(InputCleaningHelper.cleanString(author)); return author != null && playerName.equalsIgnoreCase(InputCleaningHelper.cleanString(author));
} }

View File

@@ -9,6 +9,8 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.meta.BookMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
@@ -31,7 +33,9 @@ public final class BookLoader {
* @param directory <p>The directory to save the book in</p> * @param directory <p>The directory to save the book in</p>
* @return <p>The loaded book</p> * @return <p>The loaded book</p>
*/ */
public static ItemStack loadBook(CommandSender sender, String fileName, String isSigned, String directory) { @Nullable
public static ItemStack loadBook(@NotNull CommandSender sender, @NotNull String fileName, @NotNull String isSigned,
@NotNull String directory) {
return loadBook(sender, fileName, isSigned, directory, 1); return loadBook(sender, fileName, isSigned, directory, 1);
} }
@@ -45,8 +49,14 @@ public final class BookLoader {
* @param numCopies <p>The number of copies to load</p> * @param numCopies <p>The number of copies to load</p>
* @return <p>The loaded book</p> * @return <p>The loaded book</p>
*/ */
public static ItemStack loadBook(CommandSender sender, String fileName, String isSigned, String directory, int numCopies) { @Nullable
public static ItemStack loadBook(@NotNull CommandSender sender, @NotNull String fileName, @NotNull String isSigned,
@NotNull String directory, int numCopies) {
BookDirectory bookDirectory = BookDirectory.getFromString(directory); BookDirectory bookDirectory = BookDirectory.getFromString(directory);
if (bookDirectory == null) {
BooksWithoutBorders.sendErrorMessage(sender, "Unrecognized book directory!");
return null;
}
//Find the filename if a book index is given //Find the filename if a book index is given
try { try {
@@ -96,8 +106,13 @@ public final class BookLoader {
book = new ItemStack(Material.WRITABLE_BOOK); book = new ItemStack(Material.WRITABLE_BOOK);
} }
if (bookMetadata == null) {
BooksWithoutBorders.sendErrorMessage(sender, "Unable to create blank book metadata!");
return null;
}
//Load the book from the given file //Load the book from the given file
BookToFromTextHelper.bookFromFile(file, bookMetadata); bookMetadata = BookToFromTextHelper.bookFromFile(file, bookMetadata);
if (bookMetadata == null) { if (bookMetadata == null) {
BooksWithoutBorders.sendErrorMessage(sender, "File was blank!!"); BooksWithoutBorders.sendErrorMessage(sender, "File was blank!!");
return null; return null;
@@ -131,7 +146,9 @@ public final class BookLoader {
* @param directory <p>The relative directory given</p> * @param directory <p>The relative directory given</p>
* @return <p>A file or null if it does not exist</p> * @return <p>A file or null if it does not exist</p>
*/ */
private static File getFullPath(CommandSender sender, String fileName, BookDirectory bookDirectory, String directory) { @Nullable
private static File getFullPath(@NotNull CommandSender sender, @NotNull String fileName,
@NotNull BookDirectory bookDirectory, @NotNull String directory) {
File file; File file;
String slash = BooksWithoutBordersConfig.getSlash(); String slash = BooksWithoutBordersConfig.getSlash();
String bookFolder = BooksWithoutBordersConfig.getBookFolder(); String bookFolder = BooksWithoutBordersConfig.getBookFolder();

View File

@@ -6,6 +6,8 @@ import net.knarcraft.knarlib.util.FileHelper;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.meta.BookMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
@@ -37,7 +39,7 @@ public final class BookToFromTextHelper {
* @param bookMetadata <p>Metadata about the book to save</p> * @param bookMetadata <p>Metadata about the book to save</p>
* @throws IOException <p>If unable to save the book</p> * @throws IOException <p>If unable to save the book</p>
*/ */
public static void bookToYml(String path, String fileName, BookMeta bookMetadata) throws IOException { public static void bookToYml(@NotNull String path, @NotNull String fileName, @NotNull BookMeta bookMetadata) throws IOException {
FileConfiguration bookYml = YamlConfiguration.loadConfiguration(new File(path, "blank")); FileConfiguration bookYml = YamlConfiguration.loadConfiguration(new File(path, "blank"));
if (bookMetadata.hasTitle()) { if (bookMetadata.hasTitle()) {
@@ -58,7 +60,7 @@ public final class BookToFromTextHelper {
bookYml.set("Lore", bookMetadata.getLore()); bookYml.set("Lore", bookMetadata.getLore());
} }
bookYml.save(path + fileName + ".yml"); bookYml.save(path + InputCleaningHelper.cleanString(fileName) + ".yml");
} }
/** /**
@@ -68,7 +70,8 @@ public final class BookToFromTextHelper {
* @param bookMetadata <p>The book metadata to use for saving the book</p> * @param bookMetadata <p>The book metadata to use for saving the book</p>
* @return <p>The book metadata of the loaded book</p> * @return <p>The book metadata of the loaded book</p>
*/ */
public static BookMeta bookFromFile(File file, BookMeta bookMetadata) { @Nullable
public static BookMeta bookFromFile(@NotNull File file, @NotNull BookMeta bookMetadata) {
if (file.getName().endsWith(".txt")) { if (file.getName().endsWith(".txt")) {
return bookFromTXT(file.getName(), file, bookMetadata); return bookFromTXT(file.getName(), file, bookMetadata);
} else if (file.getName().endsWith(".yml")) { } else if (file.getName().endsWith(".yml")) {
@@ -86,8 +89,8 @@ public final class BookToFromTextHelper {
* @param bookMetadata <p>Metadata about the book to save</p> * @param bookMetadata <p>Metadata about the book to save</p>
* @throws IOException <p>If unable to save the book</p> * @throws IOException <p>If unable to save the book</p>
*/ */
public static void bookToTXT(String folderPath, String fileName, BookMeta bookMetadata) throws IOException { public static void bookToTXT(@NotNull String folderPath, @NotNull String fileName, @NotNull BookMeta bookMetadata) throws IOException {
FileWriter fileWriter = new FileWriter(folderPath + fileName + ".txt", StandardCharsets.UTF_8); FileWriter fileWriter = new FileWriter(folderPath + InputCleaningHelper.cleanString(fileName) + ".txt", StandardCharsets.UTF_8);
PrintWriter printWriter = new PrintWriter(fileWriter); PrintWriter printWriter = new PrintWriter(fileWriter);
List<String> pages = bookMetadata.getPages(); List<String> pages = bookMetadata.getPages();
@@ -112,7 +115,8 @@ public final class BookToFromTextHelper {
* @param bookMetadata <p>Metadata which will be altered with the book's contents</p> * @param bookMetadata <p>Metadata which will be altered with the book's contents</p>
* @return <p>Metadata for the loaded book</p> * @return <p>Metadata for the loaded book</p>
*/ */
private static BookMeta bookFromYml(File file, BookMeta bookMetadata) { @Nullable
private static BookMeta bookFromYml(@NotNull File file, @NotNull BookMeta bookMetadata) {
try { try {
FileConfiguration bookYml = YamlConfiguration.loadConfiguration(file); FileConfiguration bookYml = YamlConfiguration.loadConfiguration(file);
@@ -135,7 +139,8 @@ public final class BookToFromTextHelper {
* @param bookMetadata <p>Metadata which will be altered with the book's contents</p> * @param bookMetadata <p>Metadata which will be altered with the book's contents</p>
* @return <p>Metadata for the loaded book</p> * @return <p>Metadata for the loaded book</p>
*/ */
private static BookMeta bookFromTXT(String fileName, File file, BookMeta bookMetadata) { @Nullable
private static BookMeta bookFromTXT(@NotNull String fileName, @NotNull File file, @NotNull BookMeta bookMetadata) {
String author; String author;
String title; String title;
String titleAuthorSeparator = BooksWithoutBordersConfig.getTitleAuthorSeparator(); String titleAuthorSeparator = BooksWithoutBordersConfig.getTitleAuthorSeparator();
@@ -159,13 +164,17 @@ public final class BookToFromTextHelper {
List<String> rawPages; List<String> rawPages;
try { try {
rawPages = readTextFile(file); rawPages = readTextFile(file);
if (rawPages == null) {
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Text file's first line was null");
return null;
}
} catch (IOException exception) { } catch (IOException exception) {
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Unable to read text file"); BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Unable to read text file");
return null; return null;
} }
//Parse the generation from the book data //Parse the generation from the book data
if (rawPages != null && !rawPages.isEmpty() && rawPages.get(0).startsWith("Generation:")) { if (!rawPages.isEmpty() && rawPages.get(0).startsWith("Generation:")) {
bookMetadata.setGeneration(BookMeta.Generation.valueOf(rawPages.get(0).split(":")[1])); bookMetadata.setGeneration(BookMeta.Generation.valueOf(rawPages.get(0).split(":")[1]));
rawPages.remove(0); rawPages.remove(0);
} }
@@ -188,7 +197,8 @@ public final class BookToFromTextHelper {
* @return <p>A string list where each string is the text on one page</p> * @return <p>A string list where each string is the text on one page</p>
* @throws IOException <p>If unable to read from the file</p> * @throws IOException <p>If unable to read from the file</p>
*/ */
private static List<String> readTextFile(File file) throws IOException { @Nullable
private static List<String> readTextFile(@NotNull File file) throws IOException {
List<String> rawPages = new ArrayList<>(); List<String> rawPages = new ArrayList<>();
BufferedReader bufferedReader = FileHelper.getBufferedReaderFromInputStream(new FileInputStream(file)); BufferedReader bufferedReader = FileHelper.getBufferedReaderFromInputStream(new FileInputStream(file));

View File

@@ -12,6 +12,7 @@ import org.bukkit.inventory.meta.BookMeta;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.ServicesManager; import org.bukkit.plugin.ServicesManager;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -32,6 +33,7 @@ public final class EconomyHelper {
* *
* @return <p>An economy instance, or null if it's not initialized</p> * @return <p>An economy instance, or null if it's not initialized</p>
*/ */
@NotNull
public static Economy getEconomy() { public static Economy getEconomy() {
return economy; return economy;
} }
@@ -67,7 +69,7 @@ public final class EconomyHelper {
* @param numCopies <p>The number of copies the player is trying to print</p> * @param numCopies <p>The number of copies the player is trying to print</p>
* @return <p>True if the player cannot pay for the printing of the books</p> * @return <p>True if the player cannot pay for the printing of the books</p>
*/ */
public static boolean cannotPayForBookPrinting(Player player, int numCopies) { public static boolean cannotPayForBookPrinting(@NotNull Player player, int numCopies) {
//BookPriceQuantity: How many items are required to pay for each book //BookPriceQuantity: How many items are required to pay for each book
//BookPriceType: Which item is used to pay for the books. AIR = use economy //BookPriceType: Which item is used to pay for the books. AIR = use economy
Material bookCurrency = BooksWithoutBordersConfig.getBookPriceType(); Material bookCurrency = BooksWithoutBordersConfig.getBookPriceType();
@@ -100,7 +102,7 @@ public final class EconomyHelper {
* @param itemCost <p>The number of writable books to pay</p> * @param itemCost <p>The number of writable books to pay</p>
* @return <p>True if the payment was successful</p> * @return <p>True if the payment was successful</p>
*/ */
private static boolean takeWritableBookPayment(Player player, int itemCost) { private static boolean takeWritableBookPayment(@NotNull Player player, int itemCost) {
List<ItemStack> books = getPlayersEmptyBooks(player); List<ItemStack> books = getPlayersEmptyBooks(player);
if (countItems(books) < itemCost) { if (countItems(books) < itemCost) {
BooksWithoutBorders.sendErrorMessage(player, itemCost + " empty " + Material.WRITABLE_BOOK + BooksWithoutBorders.sendErrorMessage(player, itemCost + " empty " + Material.WRITABLE_BOOK +
@@ -131,7 +133,7 @@ public final class EconomyHelper {
* @param items <p>The items to count</p> * @param items <p>The items to count</p>
* @return <p>The total number of items</p> * @return <p>The total number of items</p>
*/ */
private static int countItems(List<ItemStack> items) { private static int countItems(@NotNull List<ItemStack> items) {
int totalItems = 0; int totalItems = 0;
for (ItemStack itemStack : items) { for (ItemStack itemStack : items) {
totalItems += itemStack.getAmount(); totalItems += itemStack.getAmount();
@@ -145,7 +147,8 @@ public final class EconomyHelper {
* @param player <p>The player to get books for</p> * @param player <p>The player to get books for</p>
* @return <p>The empty books in the player's inventory</p> * @return <p>The empty books in the player's inventory</p>
*/ */
private static List<ItemStack> getPlayersEmptyBooks(Player player) { @NotNull
private static List<ItemStack> getPlayersEmptyBooks(@NotNull Player player) {
List<ItemStack> validBooks = new ArrayList<>(); List<ItemStack> validBooks = new ArrayList<>();
for (ItemStack itemStack : player.getInventory().getContents()) { for (ItemStack itemStack : player.getInventory().getContents()) {
if (itemStack == null || itemStack.getType() != Material.WRITABLE_BOOK) { if (itemStack == null || itemStack.getType() != Material.WRITABLE_BOOK) {
@@ -168,7 +171,7 @@ public final class EconomyHelper {
* @param numCopies <p>The number of books the player is printing</p> * @param numCopies <p>The number of books the player is printing</p>
* @return <p>True if the player had the money and it has been withdrawn</p> * @return <p>True if the player had the money and it has been withdrawn</p>
*/ */
private static boolean payForBookPrintingEconomy(Player player, double cost, int numCopies) { private static boolean payForBookPrintingEconomy(@NotNull Player player, double cost, int numCopies) {
if ((economy.getBalance(player) - cost) >= 0) { if ((economy.getBalance(player) - cost) >= 0) {
economy.withdrawPlayer(player, cost); economy.withdrawPlayer(player, cost);
BooksWithoutBorders.sendSuccessMessage(player, economy.format(cost) + " withdrawn to create " + BooksWithoutBorders.sendSuccessMessage(player, economy.format(cost) + " withdrawn to create " +
@@ -188,7 +191,7 @@ public final class EconomyHelper {
* @param player <p>The player which needs to pay</p> * @param player <p>The player which needs to pay</p>
* @param itemCost <p>The number of items to pay</p> * @param itemCost <p>The number of items to pay</p>
*/ */
private static void payForBookPrintingItem(Player player, int itemCost) { private static void payForBookPrintingItem(@NotNull Player player, int itemCost) {
PlayerInventory playerInventory = player.getInventory(); PlayerInventory playerInventory = player.getInventory();
int clearedAmount = 0; int clearedAmount = 0;

View File

@@ -11,6 +11,8 @@ import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.meta.BookMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -37,7 +39,8 @@ public final class EncryptionHelper {
* @param key <p>The key to transform</p> * @param key <p>The key to transform</p>
* @return <p>The numbers representing the key's characters</p> * @return <p>The numbers representing the key's characters</p>
*/ */
public static String getNumberKeyFromStringKey(String key) { @NotNull
public static String getNumberKeyFromStringKey(@NotNull String key) {
StringBuilder integerKey = new StringBuilder(); StringBuilder integerKey = new StringBuilder();
for (int x = 0; x < key.length(); x++) { for (int x = 0; x < key.length(); x++) {
integerKey.append(Character.getNumericValue(Character.codePointAt(key, x))); integerKey.append(Character.getNumericValue(Character.codePointAt(key, x)));
@@ -54,7 +57,9 @@ public final class EncryptionHelper {
* @param player <p>The player trying to encrypt a book</p> * @param player <p>The player trying to encrypt a book</p>
* @return <p>The pages of the book in encrypted form</p> * @return <p>The pages of the book in encrypted form</p>
*/ */
public static List<String> encryptBookPages(BookMeta book, EncryptionStyle style, String integerKey, Player player) { @Nullable
public static List<String> encryptBookPages(@NotNull BookMeta book, @NotNull EncryptionStyle style,
@NotNull String integerKey, @NotNull Player player) {
List<String> encryptedPages = new ArrayList<>(); List<String> encryptedPages = new ArrayList<>();
//Scramble the book's contents //Scramble the book's contents
if (style == EncryptionStyle.DNA) { if (style == EncryptionStyle.DNA) {
@@ -86,7 +91,9 @@ public final class EncryptionHelper {
* @param style <p>The encryption style to use</p> * @param style <p>The encryption style to use</p>
* @return <p>An encrypted version of the book</p> * @return <p>An encrypted version of the book</p>
*/ */
public static ItemStack encryptBook(Player player, boolean mainHand, String key, EncryptionStyle style) { @Nullable
public static ItemStack encryptBook(@NotNull Player player, boolean mainHand, @NotNull String key,
@NotNull EncryptionStyle style) {
return encryptBook(player, mainHand, key, style, ""); return encryptBook(player, mainHand, key, style, "");
} }
@@ -100,11 +107,17 @@ public final class EncryptionHelper {
* @param groupName <p>The name of the group to encrypt for, or "" otherwise</p> * @param groupName <p>The name of the group to encrypt for, or "" otherwise</p>
* @return <p>An encrypted version of the book</p> * @return <p>An encrypted version of the book</p>
*/ */
public static ItemStack encryptBook(Player player, boolean mainHand, String key, EncryptionStyle style, String groupName) { @Nullable
public static ItemStack encryptBook(Player player, boolean mainHand, @NotNull String key,
@NotNull EncryptionStyle style, @NotNull String groupName) {
//converts user supplied key into integer form //converts user supplied key into integer form
String integerKey = EncryptionHelper.getNumberKeyFromStringKey(key); String integerKey = EncryptionHelper.getNumberKeyFromStringKey(key);
BookMeta book = InventoryHelper.getHeldBookMetadata(player, mainHand); BookMeta book = InventoryHelper.getHeldBookMetadata(player, mainHand);
if (book == null) {
BooksWithoutBorders.sendErrorMessage(player, "Unable to get metadata from the held book!");
return null;
}
if (!book.hasPages()) { if (!book.hasPages()) {
BooksWithoutBorders.sendErrorMessage(player, "Book is empty!"); BooksWithoutBorders.sendErrorMessage(player, "Book is empty!");
@@ -143,7 +156,9 @@ public final class EncryptionHelper {
* @param newMetadata <p>The new metadata of the book</p> * @param newMetadata <p>The new metadata of the book</p>
* @return <p>An encrypted version of the book</p> * @return <p>An encrypted version of the book</p>
*/ */
private static ItemStack createEncryptedBook(BookMeta book, List<String> newPages, Player player, BookMeta newMetadata) { @NotNull
private static ItemStack createEncryptedBook(@NotNull BookMeta book, @NotNull List<String> newPages,
@NotNull Player player, @NotNull BookMeta newMetadata) {
//Create the encrypted book //Create the encrypted book
ItemStack encryptedBook = new ItemStack(Material.WRITTEN_BOOK); ItemStack encryptedBook = new ItemStack(Material.WRITTEN_BOOK);
book.setPages(newPages); book.setPages(newPages);
@@ -166,7 +181,9 @@ public final class EncryptionHelper {
* @param integerKey <p>The key used to encrypt the book</p> * @param integerKey <p>The key used to encrypt the book</p>
* @return <p>The new metadata for the book, or null if it could not be saved</p> * @return <p>The new metadata for the book, or null if it could not be saved</p>
*/ */
private static BookMeta saveBookPlaintext(String groupName, Player player, BookMeta book, String integerKey) { @Nullable
private static BookMeta saveBookPlaintext(@NotNull String groupName, @NotNull Player player,
@NotNull BookMeta book, @NotNull String integerKey) {
BookMeta newMetadata = book; BookMeta newMetadata = book;
boolean wasSaved; boolean wasSaved;
if (groupName.trim().isEmpty()) { if (groupName.trim().isEmpty()) {
@@ -190,7 +207,8 @@ public final class EncryptionHelper {
* @param deleteEncryptedFile <p>Whether to delete the plaintext file after decryption is finished</p> * @param deleteEncryptedFile <p>Whether to delete the plaintext file after decryption is finished</p>
* @return <p>The loaded book, or null if no book could be loaded</p> * @return <p>The loaded book, or null if no book could be loaded</p>
*/ */
public static ItemStack loadEncryptedBook(Player player, String key, boolean deleteEncryptedFile) { @Nullable
public static ItemStack loadEncryptedBook(@NotNull Player player, @NotNull String key, boolean deleteEncryptedFile) {
ItemStack heldBook = InventoryHelper.getHeldBook(player, true); ItemStack heldBook = InventoryHelper.getHeldBook(player, true);
BookMeta bookMetadata = (BookMeta) heldBook.getItemMeta(); BookMeta bookMetadata = (BookMeta) heldBook.getItemMeta();
String path = getBookFolder() + "Encrypted" + getSlash(); String path = getBookFolder() + "Encrypted" + getSlash();
@@ -246,7 +264,9 @@ public final class EncryptionHelper {
* @param groupName <p>The group which should be able to decrypt the book</p> * @param groupName <p>The group which should be able to decrypt the book</p>
* @return <p>The new encrypted metadata for the book, or null if encryption failed</p> * @return <p>The new encrypted metadata for the book, or null if encryption failed</p>
*/ */
private static BookMeta saveEncryptedBookForGroup(Player player, BookMeta bookMetadata, String groupName) { @Nullable
private static BookMeta saveEncryptedBookForGroup(@NotNull Player player, @NotNull BookMeta bookMetadata,
@NotNull String groupName) {
String path = getBookFolder() + "Encrypted" + getSlash() + cleanString(groupName) + getSlash(); String path = getBookFolder() + "Encrypted" + getSlash() + cleanString(groupName) + getSlash();
File dirTest = new File(path); File dirTest = new File(path);
//Creates group dir //Creates group dir
@@ -297,7 +317,8 @@ public final class EncryptionHelper {
* @param key <p>The key to use for encryption</p> * @param key <p>The key to use for encryption</p>
* @return <p>The new encrypted metadata for the book, or null if encryption failed</p> * @return <p>The new encrypted metadata for the book, or null if encryption failed</p>
*/ */
private static Boolean saveEncryptedBook(Player player, BookMeta bookMetaData, String key) { @NotNull
private static Boolean saveEncryptedBook(@NotNull Player player, @NotNull BookMeta bookMetaData, @NotNull String key) {
String path = getBookFolder() + "Encrypted" + getSlash(); String path = getBookFolder() + "Encrypted" + getSlash();
String fileName = "[" + key + "]" + BookHelper.getBookFile(bookMetaData, player, true); String fileName = "[" + key + "]" + BookHelper.getBookFile(bookMetaData, player, true);

View File

@@ -1,7 +1,12 @@
package net.knarcraft.bookswithoutborders.utility; package net.knarcraft.bookswithoutborders.utility;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* Helper class for cleaning input and names * Helper class for cleaning input and names
@@ -17,7 +22,8 @@ public final class InputCleaningHelper {
* @param list <p>The list to clean</p> * @param list <p>The list to clean</p>
* @return <p>A clean list containing all relevant values</p> * @return <p>A clean list containing all relevant values</p>
*/ */
public static List<String> cleanList(List<String> list) { @NotNull
public static List<String> cleanList(@NotNull List<String> list) {
List<String> resultList = new ArrayList<>(list); List<String> resultList = new ArrayList<>(list);
resultList.removeIf((item) -> item == null || item.trim().isEmpty()); resultList.removeIf((item) -> item == null || item.trim().isEmpty());
return resultList; return resultList;
@@ -29,7 +35,8 @@ public final class InputCleaningHelper {
* @param fileName <p>The file name to clean</p> * @param fileName <p>The file name to clean</p>
* @return <p>The cleaned file name</p> * @return <p>The cleaned file name</p>
*/ */
public static String cleanString(String fileName) { @NotNull
public static String cleanString(@NotNull String fileName) {
fileName = fileName.replace("/", ""); fileName = fileName.replace("/", "");
fileName = fileName.replace("\\", ""); fileName = fileName.replace("\\", "");
fileName = fileName.replace("*", ""); fileName = fileName.replace("*", "");
@@ -39,6 +46,7 @@ public final class InputCleaningHelper {
fileName = fileName.replace(">", ""); fileName = fileName.replace(">", "");
fileName = fileName.replace("?", ""); fileName = fileName.replace("?", "");
fileName = fileName.replace("\"", ""); fileName = fileName.replace("\"", "");
fileName = fileName.replace("§", "&");
return fileName; return fileName;
} }
@@ -49,7 +57,8 @@ public final class InputCleaningHelper {
* @param isLoading <p>Whether loading from a file as opposed to saving to a file</p> * @param isLoading <p>Whether loading from a file as opposed to saving to a file</p>
* @return <p>The fixed name</p> * @return <p>The fixed name</p>
*/ */
public static String fixName(String fileName, Boolean isLoading) { @NotNull
public static String fixName(@NotNull String fileName, @NotNull Boolean isLoading) {
if (isLoading) { if (isLoading) {
fileName = fileName.replace("_", " "); fileName = fileName.replace("_", " ");
} else { } else {
@@ -58,4 +67,41 @@ public final class InputCleaningHelper {
return fileName; return fileName;
} }
/**
* Parses a page number for a string like "page1"
*
* @param input <p>The input to parse</p>
* @return <p>The page number, or 0 if not valid</p>
*/
public static int parsePageNumber(@NotNull String input) {
try {
Pattern pattern = Pattern.compile("page([0-9]+)");
Matcher matcher = pattern.matcher(input);
if (matcher.matches()) {
return Integer.parseInt(matcher.group(1));
} else {
return 0;
}
} catch (NumberFormatException exception) {
return 0;
}
}
/**
* Parses an author specifier given in a command
*
* @param input <p>The input to parse</p>
* @return <p>The author name, or null if not an author specifier</p>
*/
@Nullable
public static String parseAuthorSpecifier(@NotNull String input) {
Pattern pattern = Pattern.compile("author([0-9a-zA-Z_]+)");
Matcher matcher = pattern.matcher(input);
if (matcher.matches()) {
return matcher.group(1);
} else {
return null;
}
}
} }

View File

@@ -1,5 +1,7 @@
package net.knarcraft.bookswithoutborders.utility; package net.knarcraft.bookswithoutborders.utility;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -40,6 +42,7 @@ public final class IntegerToRomanConverter {
* @param number <p>The number to convert</p> * @param number <p>The number to convert</p>
* @return <p>The roman representation of the number</p> * @return <p>The roman representation of the number</p>
*/ */
@NotNull
public static String getRomanNumber(int number) { public static String getRomanNumber(int number) {
StringBuilder output = new StringBuilder(); StringBuilder output = new StringBuilder();
int remainder = number; int remainder = number;
@@ -83,6 +86,7 @@ public final class IntegerToRomanConverter {
* @param times <p>The number of times to repeat the character</p> * @param times <p>The number of times to repeat the character</p>
* @return <p>The repeated string</p> * @return <p>The repeated string</p>
*/ */
@NotNull
private static String repeat(char character, int times) { private static String repeat(char character, int times) {
return String.valueOf(character).repeat(Math.max(0, times)); return String.valueOf(character).repeat(Math.max(0, times));
} }

View File

@@ -7,6 +7,8 @@ import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.meta.BookMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** /**
* The inventory helper mainly helps with getting and setting books * The inventory helper mainly helps with getting and setting books
@@ -23,7 +25,8 @@ public final class InventoryHelper {
* @param signedBook <p>Whether to check for signed or unsigned books</p> * @param signedBook <p>Whether to check for signed or unsigned books</p>
* @return <p>The book the player is holding</p> * @return <p>The book the player is holding</p>
*/ */
public static ItemStack getHeldBook(Player player, boolean signedBook) { @NotNull
public static ItemStack getHeldBook(@NotNull Player player, boolean signedBook) {
ItemSlot heldSlot = getHeldSlotBook(player, false, false, true, signedBook); ItemSlot heldSlot = getHeldSlotBook(player, false, false, true, signedBook);
if (heldSlot == ItemSlot.MAIN_HAND) { if (heldSlot == ItemSlot.MAIN_HAND) {
return getHeldItem(player, true); return getHeldItem(player, true);
@@ -40,7 +43,7 @@ public final class InventoryHelper {
* @param player <p>The player holding the book</p> * @param player <p>The player holding the book</p>
* @param newBook <p>The new book the player should hold</p> * @param newBook <p>The new book the player should hold</p>
*/ */
public static void setHeldWrittenBook(Player player, ItemStack newBook) { public static void setHeldWrittenBook(@NotNull Player player, @NotNull ItemStack newBook) {
ItemSlot itemSlot = getHeldSlotBook(player, false, false, true, true); ItemSlot itemSlot = getHeldSlotBook(player, false, false, true, true);
if (itemSlot == ItemSlot.MAIN_HAND) { if (itemSlot == ItemSlot.MAIN_HAND) {
replaceHeldItem(player, newBook, true); replaceHeldItem(player, newBook, true);
@@ -59,7 +62,8 @@ public final class InventoryHelper {
* @param twoBooksMessage <p>The message to display if the player is holding one book in each hand</p> * @param twoBooksMessage <p>The message to display if the player is holding one book in each hand</p>
* @return <p>False if the player is holding exactly one book</p> * @return <p>False if the player is holding exactly one book</p>
*/ */
public static boolean notHoldingOneWritableBookCheck(Player player, String noBookMessage, String twoBooksMessage) { public static boolean notHoldingOneWritableBookCheck(@NotNull Player player, @NotNull String noBookMessage,
@NotNull String twoBooksMessage) {
BookHoldingState holdingState = getBookHoldingState(player); BookHoldingState holdingState = getBookHoldingState(player);
if (holdingState == BookHoldingState.NONE || holdingState == BookHoldingState.SIGNED_BOTH_HANDS || if (holdingState == BookHoldingState.NONE || holdingState == BookHoldingState.SIGNED_BOTH_HANDS ||
@@ -84,7 +88,8 @@ public final class InventoryHelper {
* @param twoBooksMessage <p>The message to display if the player is holding one book in each hand</p> * @param twoBooksMessage <p>The message to display if the player is holding one book in each hand</p>
* @return <p>False if the player is holding exactly one book</p> * @return <p>False if the player is holding exactly one book</p>
*/ */
public static boolean notHoldingOneWrittenBookCheck(Player player, String noBookMessage, String twoBooksMessage) { public static boolean notHoldingOneWrittenBookCheck(@NotNull Player player, @NotNull String noBookMessage,
@NotNull String twoBooksMessage) {
BookHoldingState holdingState = getBookHoldingState(player); BookHoldingState holdingState = getBookHoldingState(player);
if (holdingState == BookHoldingState.NONE || holdingState == BookHoldingState.UNSIGNED_BOTH_HANDS || if (holdingState == BookHoldingState.NONE || holdingState == BookHoldingState.UNSIGNED_BOTH_HANDS ||
@@ -111,7 +116,8 @@ public final class InventoryHelper {
* @param writtenBook <p>Whether to search for written or unwritten books, if it's relevant</p> * @param writtenBook <p>Whether to search for written or unwritten books, if it's relevant</p>
* @return <p>The slot of the player's held book</p> * @return <p>The slot of the player's held book</p>
*/ */
public static ItemSlot getHeldSlotBook(Player player, boolean handMatters, boolean mainHand, @NotNull
public static ItemSlot getHeldSlotBook(@NotNull Player player, boolean handMatters, boolean mainHand,
boolean typeMatters, boolean writtenBook) { boolean typeMatters, boolean writtenBook) {
BookHoldingState state = getBookHoldingState(player); BookHoldingState state = getBookHoldingState(player);
ItemStack mainHandItem = getHeldItem(player, true); ItemStack mainHandItem = getHeldItem(player, true);
@@ -168,7 +174,8 @@ public final class InventoryHelper {
* @param player <p>The player to check</p> * @param player <p>The player to check</p>
* @return <p>The state of the player's book holding</p> * @return <p>The state of the player's book holding</p>
*/ */
private static BookHoldingState getBookHoldingState(Player player) { @NotNull
private static BookHoldingState getBookHoldingState(@NotNull Player player) {
ItemStack mainHandItem = getHeldItem(player, true); ItemStack mainHandItem = getHeldItem(player, true);
ItemStack offHandItem = getHeldItem(player, false); ItemStack offHandItem = getHeldItem(player, false);
@@ -205,7 +212,8 @@ public final class InventoryHelper {
* @param mainHand <p>Whether to get information about a book in the player's main hand or off hand</p> * @param mainHand <p>Whether to get information about a book in the player's main hand or off hand</p>
* @return <p>Information about the held book</p> * @return <p>Information about the held book</p>
*/ */
public static BookMeta getHeldBookMetadata(Player player, boolean mainHand) { @Nullable
public static BookMeta getHeldBookMetadata(@NotNull Player player, boolean mainHand) {
return (BookMeta) getHeldItem(player, mainHand).getItemMeta(); return (BookMeta) getHeldItem(player, mainHand).getItemMeta();
} }
@@ -216,7 +224,8 @@ public final class InventoryHelper {
* @param mainHand <p>Whether to get the item in the player's main hand or off hand</p> * @param mainHand <p>Whether to get the item in the player's main hand or off hand</p>
* @return <p>The item the player is holding in the given hand</p> * @return <p>The item the player is holding in the given hand</p>
*/ */
public static ItemStack getHeldItem(Player player, boolean mainHand) { @NotNull
public static ItemStack getHeldItem(@NotNull Player player, boolean mainHand) {
if (mainHand) { if (mainHand) {
return player.getInventory().getItemInMainHand(); return player.getInventory().getItemInMainHand();
} else { } else {
@@ -231,7 +240,7 @@ public final class InventoryHelper {
* @param newBook <p>The new book the player should hold</p> * @param newBook <p>The new book the player should hold</p>
* @param mainHand <p>Whether to replace the item in the player's main hand or off hand</p> * @param mainHand <p>Whether to replace the item in the player's main hand or off hand</p>
*/ */
public static void replaceHeldItem(Player player, ItemStack newBook, boolean mainHand) { public static void replaceHeldItem(@NotNull Player player, @NotNull ItemStack newBook, boolean mainHand) {
if (mainHand) { if (mainHand) {
player.getInventory().setItemInMainHand(newBook); player.getInventory().setItemInMainHand(newBook);
} else { } else {

View File

@@ -1,5 +1,7 @@
package net.knarcraft.bookswithoutborders.utility; package net.knarcraft.bookswithoutborders.utility;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -16,6 +18,7 @@ public final class TabCompletionTypeHelper {
* *
* @return <p>A list of booleans</p> * @return <p>A list of booleans</p>
*/ */
@NotNull
public static List<String> getBooleans() { public static List<String> getBooleans() {
List<String> booleans = new ArrayList<>(); List<String> booleans = new ArrayList<>();
booleans.add("true"); booleans.add("true");
@@ -30,6 +33,7 @@ public final class TabCompletionTypeHelper {
* @param end <p>The end number</p> * @param end <p>The end number</p>
* @return <p>A list of numbers</p> * @return <p>A list of numbers</p>
*/ */
@NotNull
public static List<String> getNumbers(int start, int end) { public static List<String> getNumbers(int start, int end) {
List<String> numbers = new ArrayList<>(); List<String> numbers = new ArrayList<>();
for (int i = start; i <= end; i++) { for (int i = start; i <= end; i++) {
@@ -45,6 +49,7 @@ public final class TabCompletionTypeHelper {
* @param end <p>The end number</p> * @param end <p>The end number</p>
* @return <p>A list of booleans and numbers</p> * @return <p>A list of booleans and numbers</p>
*/ */
@NotNull
public static List<String> getBooleansAndNumbers(int start, int end) { public static List<String> getBooleansAndNumbers(int start, int end) {
List<String> booleansAndNumbers = new ArrayList<>(); List<String> booleansAndNumbers = new ArrayList<>();
List<String> booleans = getBooleans(); List<String> booleans = getBooleans();

View File

@@ -97,6 +97,10 @@ commands:
description: Reloads BwB's configuration file description: Reloads BwB's configuration file
usage: /<command> usage: /<command>
permission: bookswithoutborders.reload permission: bookswithoutborders.reload
setBookshelfData:
description: Sets custom data for a chiseled bookshelf used when peeking at the bookshelf
usage: /<command> <delete/name/lore> <text> [more text]
permission: bookswithoutborders.editbookshelf
permissions: permissions:
bookswithoutborders.*: bookswithoutborders.*:
description: Grants all permissions description: Grants all permissions
@@ -125,6 +129,7 @@ permissions:
bookswithoutborders.setbookprice: true bookswithoutborders.setbookprice: true
bookswithoutborders.reload: true bookswithoutborders.reload: true
bookswithoutborders.setgeneration: true bookswithoutborders.setgeneration: true
bookswithoutborders.editbookshelf: true
bookswithoutborders.use: bookswithoutborders.use:
description: Allows player to use commands to save/load/delete in their personal directory, and peeking at bookshelves if enabled description: Allows player to use commands to save/load/delete in their personal directory, and peeking at bookshelves if enabled
children: children:
@@ -194,3 +199,5 @@ permissions:
description: Allows player to change the generation of a book (Original, Copy, Copy of Copy) description: Allows player to change the generation of a book (Original, Copy, Copy of Copy)
bookswithoutborders.peekbookshelf: bookswithoutborders.peekbookshelf:
description: Allows player to left-click a bookshelf to see the contents of the shelf description: Allows player to left-click a bookshelf to see the contents of the shelf
bookswithoutborders.editbookshelf:
description: Allows player to set name/lore for bookshelves, used for peeking

View File

@@ -3,6 +3,7 @@ package net.knarcraft.bookswithoutborders.encryption;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNotSame;
public class AESTest { public class AESTest {
@@ -16,6 +17,7 @@ public class AESTest {
String encrypted = aes.encryptDecryptText(plainText, password, true); String encrypted = aes.encryptDecryptText(plainText, password, true);
assertNotSame(encrypted, plainText); assertNotSame(encrypted, plainText);
assertNotNull(encrypted);
String decrypted = aes.encryptDecryptText(encrypted, password, false); String decrypted = aes.encryptDecryptText(encrypted, password, false);
assertEquals(plainText, decrypted); assertEquals(plainText, decrypted);
} }