Makes a lot of formatting customizable, and fixes some problems caused by splitting Translatable
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good

This commit is contained in:
2025-08-20 13:38:10 +02:00
parent 887cc72f0d
commit b01523f058
23 changed files with 315 additions and 98 deletions

View File

@@ -145,7 +145,7 @@ public class CommandBooksWithoutBorders implements TabExecutor {
PluginCommand pluginCommand = BooksWithoutBorders.getInstance().getCommand(bwBCommand.toString()); PluginCommand pluginCommand = BooksWithoutBorders.getInstance().getCommand(bwBCommand.toString());
if (pluginCommand == null) { if (pluginCommand == null) {
BooksWithoutBorders.log(Level.SEVERE, StringFormatter.replacePlaceholder( BooksWithoutBorders.log(Level.SEVERE, StringFormatter.replacePlaceholder(
StaticMessage.COMMAND_NOT_REGISTERED.toString(), "{command}", bwBCommand.toString())); StaticMessage.EXCEPTION_COMMAND_NOT_REGISTERED.toString(), "{command}", bwBCommand.toString()));
return ""; return "";
} }

View File

@@ -40,7 +40,7 @@ public class CommandSetLore implements TabExecutor {
ItemStack heldItem = InventoryHelper.getHeldItem(player, true); ItemStack heldItem = InventoryHelper.getHeldItem(player, true);
if (heldItem.getType() == Material.AIR) { if (heldItem.getType() == Material.AIR) {
stringFormatter.displayErrorMessage(player, Translatable.ERROR_LORE_NO_ITEM); stringFormatter.displayErrorMessage(player, Translatable.ERROR_NO_ITEM);
return false; return false;
} }

View File

@@ -40,7 +40,7 @@ public class CommandSetTitle implements TabExecutor {
ItemStack heldItem = InventoryHelper.getHeldItem(player, true); ItemStack heldItem = InventoryHelper.getHeldItem(player, true);
if (heldItem.getType() == Material.AIR) { if (heldItem.getType() == Material.AIR) {
BooksWithoutBorders.sendErrorMessage(sender, "You must be holding an item to set title!"); stringFormatter.displayErrorMessage(sender, Translatable.ERROR_NO_ITEM);
return false; return false;
} }
@@ -49,7 +49,7 @@ public class CommandSetTitle implements TabExecutor {
ItemMeta itemMetadata = heldItem.getItemMeta(); ItemMeta itemMetadata = heldItem.getItemMeta();
if (itemMetadata == null) { if (itemMetadata == null) {
BooksWithoutBorders.sendErrorMessage(sender, "Unable to get metadata for your held item!"); stringFormatter.displayErrorMessage(sender, Translatable.ERROR_METADATA_MISSING);
return false; return false;
} }
@@ -57,7 +57,7 @@ public class CommandSetTitle implements TabExecutor {
ItemMeta newMetaData; ItemMeta newMetaData;
if (heldItem.getType() == Material.WRITTEN_BOOK) { if (heldItem.getType() == Material.WRITTEN_BOOK) {
if (title.length() > 32) { if (title.length() > 32) {
BooksWithoutBorders.sendErrorMessage(sender, "Book titles are capped at 32 characters!"); stringFormatter.displayErrorMessage(sender, Translatable.ERROR_TITLE_LENGTH);
return false; return false;
} }
BookMeta bookMetadata = (BookMeta) itemMetadata; BookMeta bookMetadata = (BookMeta) itemMetadata;
@@ -70,7 +70,8 @@ public class CommandSetTitle implements TabExecutor {
//Set the new metadata //Set the new metadata
heldItem.setItemMeta(newMetaData); heldItem.setItemMeta(newMetaData);
BooksWithoutBorders.sendSuccessMessage(sender, "Title set to " + title + "!"); stringFormatter.displaySuccessMessage(sender,
stringFormatter.replacePlaceholder(Translatable.SUCCESS_TITLE_SET, "{title}", title));
return true; return true;
} }

View File

@@ -1,9 +1,12 @@
package net.knarcraft.bookswithoutborders.command; package net.knarcraft.bookswithoutborders.command;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders; import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.Permission;
import net.knarcraft.bookswithoutborders.config.translation.Translatable;
import net.knarcraft.bookswithoutborders.state.ItemSlot; import net.knarcraft.bookswithoutborders.state.ItemSlot;
import net.knarcraft.bookswithoutborders.utility.BookHelper; import net.knarcraft.bookswithoutborders.utility.BookHelper;
import net.knarcraft.bookswithoutborders.utility.InventoryHelper; import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
import net.knarcraft.knarlib.formatting.StringFormatter;
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;
@@ -24,13 +27,17 @@ public class CommandUnSign 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[] arguments) { @NotNull String[] arguments) {
StringFormatter stringFormatter = BooksWithoutBorders.getStringFormatter();
if (!(sender instanceof Player player)) { if (!(sender instanceof Player player)) {
BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!"); stringFormatter.displayErrorMessage(sender, Translatable.ERROR_PLAYER_ONLY);
return false; return false;
} }
if (InventoryHelper.notHoldingOneWrittenBookCheck(player, "You must be holding a signed book to unsign it!", if (InventoryHelper.notHoldingOneWrittenBookCheck(player,
"You cannot unsign two books at once. Please un-equip one of the books you're holding!")) { stringFormatter.replacePlaceholder(Translatable.ERROR_NOT_HOLDING_WRITTEN_BOOK, "{action}",
stringFormatter.getUnFormattedColoredMessage(Translatable.ACTION_UNSIGN)),
stringFormatter.replacePlaceholder(Translatable.ERROR_ONLY_ONE_BOOK, "{action}",
stringFormatter.getUnFormattedColoredMessage(Translatable.ACTION_UNSIGN)))) {
return false; return false;
} }
@@ -50,14 +57,14 @@ public class CommandUnSign implements TabExecutor {
//Get the old book //Get the old book
BookMeta oldMetadata = InventoryHelper.getHeldBookMetadata(player, mainHand); BookMeta oldMetadata = InventoryHelper.getHeldBookMetadata(player, mainHand);
if (oldMetadata == null) { if (oldMetadata == null) {
BooksWithoutBorders.sendErrorMessage(player, "Unable to get metadata from the held book!"); BooksWithoutBorders.getStringFormatter().displayErrorMessage(player, Translatable.ERROR_METADATA_MISSING);
return; 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
if (BooksWithoutBorders.getConfiguration().getAuthorOnlyUnsign() && if (BooksWithoutBorders.getConfiguration().getAuthorOnlyUnsign() &&
!player.hasPermission("bookswithoutborders.bypassAuthorOnlyUnsign")) { !player.hasPermission(Permission.AUTHOR_ONLY_UNSIGN.toString())) {
if (BookHelper.isNotAuthor(player, Objects.requireNonNull(oldMetadata))) { if (BookHelper.isNotAuthor(player, Objects.requireNonNull(oldMetadata))) {
return; return;
} }

View File

@@ -37,6 +37,10 @@ public enum Permission {
*/ */
BYPASS_AUTHOR_ONLY_SAVE("bypassAuthorOnlySave"), BYPASS_AUTHOR_ONLY_SAVE("bypassAuthorOnlySave"),
/**
* The permission for bypassing author only un-signing
*/
AUTHOR_ONLY_UNSIGN("bypassAuthorOnlyUnsign"),
; ;
private final @NotNull String node; private final @NotNull String node;

View File

@@ -9,7 +9,7 @@ public enum StaticMessage {
BOOK_SAVING_FAILED("Saving failed! Aborting..."), BOOK_SAVING_FAILED("Saving failed! Aborting..."),
BOOK_FOLDER_CREATE_FAILED("Unable to create necessary folders"), BOOK_FOLDER_CREATE_FAILED("Unable to create necessary folders"),
COMMAND_NOT_REGISTERED("Command {command} has not been registered!"), EXCEPTION_COMMAND_NOT_REGISTERED("Command {command} has not been registered!"),
EXCEPTION_VAULT_NOT_AVAILABLE("Vault is unavailable, but book price is set to economy. Unsetting book cost!"), EXCEPTION_VAULT_NOT_AVAILABLE("Vault is unavailable, but book price is set to economy. Unsetting book cost!"),
EXCEPTION_VAULT_PRICE_NOT_CHANGED("BooksWithoutBorders failed to hook into Vault! Book price not set!"), EXCEPTION_VAULT_PRICE_NOT_CHANGED("BooksWithoutBorders failed to hook into Vault! Book price not set!"),
EXCEPTION_ENCRYPTED_FILE_DELETE_FAILED("Book encryption data failed to delete upon decryption!\nFile location: {path}"), EXCEPTION_ENCRYPTED_FILE_DELETE_FAILED("Book encryption data failed to delete upon decryption!\nFile location: {path}"),
@@ -17,6 +17,14 @@ public enum StaticMessage {
EXCEPTION_SAVE_BOOK_FAILED("Unable to save book"), EXCEPTION_SAVE_BOOK_FAILED("Unable to save book"),
EXCEPTION_BOOKSHELF_SAVING_FAILED("Unable to save bookshelves!"), EXCEPTION_BOOKSHELF_SAVING_FAILED("Unable to save bookshelves!"),
NOTICE_NO_BOOKSHELVES("BooksWithoutBorders found no bookshelves to load"), NOTICE_NO_BOOKSHELVES("BooksWithoutBorders found no bookshelves to load"),
EXCEPTION_BOOKSHELF_NAME_EMPTY("Bookshelves cannot have empty titles!"),
DEBUG_AES_INVALID_KEY("Invalid AES key given!"),
DEBUG_AES_INVALID_PARAMETERS("Invalid AES parameters given!"),
DEBUG_AES_INVALID_BLOCK_SIZE("Invalid AES block size during finalization"),
DEBUG_AES_INVALID_PADDING_FINALIZATION("Invalid AES padding during finalization"),
DEBUG_AES_INVALID_ALGORITHM("Invalid AES algorithm"),
DEBUG_AES_INVALID_PADDING_CIPHER("Invalid AES padding during Cipher generation"),
DEBUG_AES_INVALID_KEY_SPECIFICATION("Invalid AES key specification"),
; ;
private final @NotNull String messageString; private final @NotNull String messageString;

View File

@@ -41,7 +41,7 @@ public enum BookshelfMessage implements TranslatableMessage {
@Override @Override
public @NotNull TranslatableMessage[] getAllMessages() { public @NotNull TranslatableMessage[] getAllMessages() {
return Translatable.values(); return BookshelfMessage.values();
} }
} }

View File

@@ -51,7 +51,7 @@ public enum CostMessage implements TranslatableMessage {
@Override @Override
public @NotNull TranslatableMessage[] getAllMessages() { public @NotNull TranslatableMessage[] getAllMessages() {
return Translatable.values(); return CostMessage.values();
} }
} }

View File

@@ -67,11 +67,91 @@ public enum Formatting implements TranslatableMessage {
* The format used when formatting text on a title page * The format used when formatting text on a title page
*/ */
NEUTRAL_TITLE_PAGE_TEXT_FORMAT, NEUTRAL_TITLE_PAGE_TEXT_FORMAT,
/**
* The format used when showing the header for public books by a specific author
*/
NEUTRAL_AUTHOR_PUBLIC_BOOKS_HEADER,
/**
* The format used when showing the header for player books by a specific author
*/
NEUTRAL_AUTHOR_PLAYER_BOOKS_HEADER,
/**
* The format used when showing the hover action for selecting a book by its path
*/
NEUTRAL_AUTHOR_BOOKS_PATH,
/**
* The format used when displaying an empty author books page
*/
NEUTRAL_AUTHOR_BOOKS_INVALID_PAGE,
/**
* The format used when showing current ant total pages in the book list
*/
NEUTRAL_BOOK_LIST_TOTAL_PAGES,
/**
* The format used when showing the previous button in the book list
*/
NEUTRAL_BOOK_LIST_PREVIOUS_PAGE,
/**
* The format used when showing the next button in the book list
*/
NEUTRAL_BOOK_LIST_NEXT_PAGE,
/**
* The separator between book and author in the book list
*/
NEUTRAL_BOOK_LIST_AUTHOR_SEPARATOR,
/**
* The format used when showing the page a link goes to in the book list
*/
NEUTRAL_BOOK_LIST_TO_PAGE,
/**
* The format used when showing the hover hint on the book index
*/
NEUTRAL_BOOK_LIST_INDEX_HOVER,
/**
* The format used when showing the header of the public book list
*/
NEUTRAL_BOOK_LIST_PUBLIC_BOOKS_HEADER,
/**
* The format used when showing the header of the player book list
*/
NEUTRAL_BOOK_LIST_PLAYER_BOOKS_HEADER,
/**
* The format used when showing the books by hover text
*/
NEUTRAL_BOOK_LIST_AUTHOR_HOVER,
/**
* The format used when showing the book path hover text
*/
NEUTRAL_BOOK_LIST_PATH_HOVER,
/**
* The format used when showing the book list select by index hover text
*/
NEUTRAL_BOOK_LIST_BOOK_INDEX_HOVER,
/**
* The format used when showing book int index in the book list
*/
NEUTRAL_BOOK_LIST_BOOK_INDEX_NUMBER,
; ;
@Override @Override
public @NotNull TranslatableMessage[] getAllMessages() { public @NotNull TranslatableMessage[] getAllMessages() {
return Translatable.values(); return Formatting.values();
} }
} }

View File

@@ -41,7 +41,7 @@ public enum GiveMessage implements TranslatableMessage {
@Override @Override
public @NotNull TranslatableMessage[] getAllMessages() { public @NotNull TranslatableMessage[] getAllMessages() {
return Translatable.values(); return GiveMessage.values();
} }
} }

View File

@@ -41,7 +41,7 @@ public enum SaveMessage implements TranslatableMessage {
@Override @Override
public @NotNull TranslatableMessage[] getAllMessages() { public @NotNull TranslatableMessage[] getAllMessages() {
return Translatable.values(); return SaveMessage.values();
} }
} }

View File

@@ -78,6 +78,11 @@ public enum Translatable implements TranslatableMessage {
*/ */
SUCCESS_LORE_SET, SUCCESS_LORE_SET,
/**
* The success message displayed when an item's title is successfully set
*/
SUCCESS_TITLE_SET,
/** /**
* The error to display when the console attempts to run a player-only command * The error to display when the console attempts to run a player-only command
*/ */
@@ -128,6 +133,11 @@ public enum Translatable implements TranslatableMessage {
*/ */
ACTION_CHANGE_GENERATION, ACTION_CHANGE_GENERATION,
/**
* The translation of the unsign action
*/
ACTION_UNSIGN,
/** /**
* The error displayed when running a relevant command while holding one book in each hand * The error displayed when running a relevant command while holding one book in each hand
*/ */
@@ -291,12 +301,17 @@ public enum Translatable implements TranslatableMessage {
/** /**
* The error displayed when attempting to change the lore of an item without holding an item * The error displayed when attempting to change the lore of an item without holding an item
*/ */
ERROR_LORE_NO_ITEM, ERROR_NO_ITEM,
/** /**
* The error displayed when attempting to change an item's title/display name without specifying the new title/display name * The error displayed when attempting to change an item's title/display name without specifying the new title/display name
*/ */
ERROR_TITLE_EMPTY, ERROR_TITLE_EMPTY,
/**
* The error displayed when attempting to change a book's title with a title that's too long
*/
ERROR_TITLE_LENGTH,
; ;
@Override @Override

View File

@@ -1,5 +1,6 @@
package net.knarcraft.bookswithoutborders.container; package net.knarcraft.bookswithoutborders.container;
import net.knarcraft.bookswithoutborders.config.StaticMessage;
import org.bukkit.Location; import org.bukkit.Location;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -64,7 +65,7 @@ public class Bookshelf {
*/ */
public void setTitle(@NotNull String title) { public void setTitle(@NotNull String title) {
if (title.isBlank()) { if (title.isBlank()) {
throw new IllegalArgumentException("Bookshelves cannot have empty titles!"); throw new IllegalArgumentException(StaticMessage.EXCEPTION_BOOKSHELF_NAME_EMPTY.toString());
} }
this.title = title; this.title = title;
} }

View File

@@ -1,6 +1,7 @@
package net.knarcraft.bookswithoutborders.encryption; package net.knarcraft.bookswithoutborders.encryption;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders; import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.StaticMessage;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -97,8 +98,11 @@ public class AES implements Encryptor {
//Initialize cipher //Initialize cipher
try { try {
aes.init(mode, secretKeySpec, ivParameterSpec); aes.init(mode, secretKeySpec, ivParameterSpec);
} catch (InvalidKeyException | InvalidAlgorithmParameterException exception) { } catch (InvalidKeyException exception) {
BooksWithoutBorders.log(Level.SEVERE, "Invalid AES input given!"); BooksWithoutBorders.log(Level.FINE, StaticMessage.DEBUG_AES_INVALID_KEY.toString());
return null;
} catch (InvalidAlgorithmParameterException exception) {
BooksWithoutBorders.log(Level.FINE, StaticMessage.DEBUG_AES_INVALID_PARAMETERS.toString());
return null; return null;
} }
//Perform encryption/decryption and output result //Perform encryption/decryption and output result
@@ -106,10 +110,10 @@ public class AES implements Encryptor {
byte[] output = aes.doFinal(getInputBytes(input, encrypt)); byte[] output = aes.doFinal(getInputBytes(input, encrypt));
return createResult(output, encrypt); return createResult(output, encrypt);
} catch (IllegalBlockSizeException exception) { } catch (IllegalBlockSizeException exception) {
BooksWithoutBorders.log(Level.SEVERE, "Invalid AES block size during finalization"); BooksWithoutBorders.log(Level.FINE, StaticMessage.DEBUG_AES_INVALID_BLOCK_SIZE.toString());
return null; return null;
} catch (BadPaddingException exception) { } catch (BadPaddingException exception) {
BooksWithoutBorders.log(Level.SEVERE, "Invalid AES padding during finalization"); BooksWithoutBorders.log(Level.FINE, StaticMessage.DEBUG_AES_INVALID_PADDING_FINALIZATION.toString());
return null; return null;
} }
} }
@@ -156,10 +160,10 @@ public class AES implements Encryptor {
try { try {
aes = Cipher.getInstance("AES/CBC/PKCS5Padding"); aes = Cipher.getInstance("AES/CBC/PKCS5Padding");
} catch (NoSuchAlgorithmException exception) { } catch (NoSuchAlgorithmException exception) {
BooksWithoutBorders.log(Level.SEVERE, "Invalid AES algorithm during Cipher generation"); BooksWithoutBorders.log(Level.SEVERE, StaticMessage.DEBUG_AES_INVALID_ALGORITHM.toString());
return null; return null;
} catch (NoSuchPaddingException exception) { } catch (NoSuchPaddingException exception) {
BooksWithoutBorders.log(Level.SEVERE, "Invalid AES padding during Cipher generation"); BooksWithoutBorders.log(Level.SEVERE, StaticMessage.DEBUG_AES_INVALID_PADDING_CIPHER.toString());
return null; return null;
} }
return aes; return aes;
@@ -178,14 +182,14 @@ public class AES implements Encryptor {
try { try {
keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
} catch (NoSuchAlgorithmException exception) { } catch (NoSuchAlgorithmException exception) {
BooksWithoutBorders.log(Level.SEVERE, "Invalid AES algorithm"); BooksWithoutBorders.log(Level.SEVERE, StaticMessage.DEBUG_AES_INVALID_ALGORITHM.toString());
return null; return null;
} }
SecretKey tmp; SecretKey tmp;
try { try {
tmp = keyFactory.generateSecret(spec); tmp = keyFactory.generateSecret(spec);
} catch (InvalidKeySpecException exception) { } catch (InvalidKeySpecException exception) {
BooksWithoutBorders.log(Level.SEVERE, "Invalid AES key specification"); BooksWithoutBorders.log(Level.SEVERE, StaticMessage.DEBUG_AES_INVALID_KEY_SPECIFICATION.toString());
return null; return null;
} }
return new SecretKeySpec(tmp.getEncoded(), "AES"); return new SecretKeySpec(tmp.getEncoded(), "AES");

View File

@@ -12,6 +12,8 @@ import java.util.Random;
* *
* <p>Not sure where this was gotten from, but it does exist at * <p>Not sure where this was gotten from, but it does exist at
* <a href="https://crypto.stackexchange.com/questions/11614/how-do-i-test-my-encryption-absolute-amateur">Stack Exchange</a>.</p> * <a href="https://crypto.stackexchange.com/questions/11614/how-do-i-test-my-encryption-absolute-amateur">Stack Exchange</a>.</p>
*
* @author AkiraAkiba
*/ */
public class GenenCrypt implements Encryptor { public class GenenCrypt implements Encryptor {
@@ -58,11 +60,12 @@ public class GenenCrypt implements Encryptor {
originalCodonList.remove(index); originalCodonList.remove(index);
} }
// define the characters that can be encoded, 64 in total /* define the characters that can be encoded, 64 in total
// 26 capital letters 26 capital letters
// 10 digits 10 digits
// space, newline, and tab space, newline, and tab
// the symbols . , ? " ! @ # $ % ^ & * ( ) - + = / _ \ : ; < > the symbols . , ? " ! @ # $ % ^ & * ( ) - + = / _ \ : ; < >
*/
availableCharacters = new String[]{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", availableCharacters = new String[]{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
"R", "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", " ", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", " ",
"\t", "\n", ".", ",", "?", "\"", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "-", "+", "=", "/", "\t", "\n", ".", ",", "?", "\"", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "-", "+", "=", "/",

View File

@@ -5,6 +5,8 @@ import org.jetbrains.annotations.Nullable;
/** /**
* So-called "Magic" encryption which simply makes the contents unreadable * So-called "Magic" encryption which simply makes the contents unreadable
*
* @author AkiraAkiba
*/ */
public class Magic implements Encryptor { public class Magic implements Encryptor {

View File

@@ -10,6 +10,8 @@ import java.util.Base64;
/** /**
* A one-time pad implementation * A one-time pad implementation
*
* @author AkiraAkiba
*/ */
public class OneTimePad implements Encryptor { public class OneTimePad implements Encryptor {

View File

@@ -8,6 +8,8 @@ import java.util.StringTokenizer;
/** /**
* A simple substitution cipher * A simple substitution cipher
*
* @author AkiraAkiba
*/ */
public class SubstitutionCipher implements Encryptor { public class SubstitutionCipher implements Encryptor {

View File

@@ -1,8 +1,10 @@
package net.knarcraft.bookswithoutborders.gui; package net.knarcraft.bookswithoutborders.gui;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders; import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.translation.Formatting;
import net.knarcraft.bookswithoutborders.utility.BookFileHelper; import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
import net.knarcraft.bookswithoutborders.utility.BookFormatter; import net.knarcraft.bookswithoutborders.utility.BookFormatter;
import net.knarcraft.knarlib.formatting.TranslatableMessage;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder; import net.md_5.bungee.api.chat.ComponentBuilder;
@@ -33,9 +35,9 @@ public class AuthorBookIndex extends BookIndex {
int totalPages = (int) Math.ceil((double) availableBooks.size() / booksPerPage); int totalPages = (int) Math.ceil((double) availableBooks.size() / booksPerPage);
if (page > totalPages) { if (page > totalPages) {
sender.sendMessage(ChatColor.GRAY + "No such page"); BooksWithoutBorders.getStringFormatter().displayErrorMessage(sender, Formatting.NEUTRAL_AUTHOR_BOOKS_INVALID_PAGE);
} else { } else {
showAuthorBooks(sender, command, page, totalPages, availableBooks, authorName); showAuthorBooks(sender, command, page, totalPages, availableBooks, authorName, listPublic);
} }
} }
@@ -48,19 +50,16 @@ public class AuthorBookIndex extends BookIndex {
* @param totalPages <p>The total amount of pages</p> * @param totalPages <p>The total amount of pages</p>
* @param availableBooks <p>All books available to the sender</p> * @param availableBooks <p>All books available to the sender</p>
* @param authorName <p>The name of the author currently shown</p> * @param authorName <p>The name of the author currently shown</p>
* @param listPublic <p>Whether to display public books</p>
*/ */
private static void showAuthorBooks(@NotNull CommandSender sender, @NotNull String command, int page, private static void showAuthorBooks(@NotNull CommandSender sender, @NotNull String command, int page,
int totalPages, @NotNull List<String> availableBooks, @NotNull String authorName) { int totalPages, @NotNull List<String> availableBooks,
@NotNull String authorName, boolean listPublic) {
ComponentBuilder componentBuilder = new ComponentBuilder(); ComponentBuilder componentBuilder = new ComponentBuilder();
String navigationCommand = command + " author" + authorName; String navigationCommand = command + " author" + authorName;
componentBuilder.append("--- "); TranslatableMessage message = listPublic ? Formatting.NEUTRAL_AUTHOR_PUBLIC_BOOKS_HEADER : Formatting.NEUTRAL_AUTHOR_PLAYER_BOOKS_HEADER;
if (command.toLowerCase().contains("public")) { componentBuilder.append(color(message, "{author}", authorName));
componentBuilder.append("Publicly saved books by: ").color(ChatColor.GREEN).append(authorName).color(ChatColor.AQUA);
} else {
componentBuilder.append("Your saved books by: ").color(ChatColor.GREEN).append(authorName).color(ChatColor.AQUA);
}
componentBuilder.append(" ---", ComponentBuilder.FormatRetention.NONE).append("\n");
displayBookList(componentBuilder, command, page, availableBooks); displayBookList(componentBuilder, command, page, availableBooks);
@@ -84,10 +83,15 @@ public class AuthorBookIndex extends BookIndex {
@NotNull List<String> availableBooks) { @NotNull List<String> availableBooks) {
int startIndex = (page - 1) * booksPerPage; int startIndex = (page - 1) * booksPerPage;
for (int bookIndex = startIndex; bookIndex < Math.min(startIndex + booksPerPage, availableBooks.size()); bookIndex++) { for (int bookIndex = startIndex; bookIndex < Math.min(startIndex + booksPerPage, availableBooks.size()); bookIndex++) {
componentBuilder.append(getNiceName(availableBooks.get(bookIndex))).color(ChatColor.WHITE).event( String title = BookFileHelper.getBookTitleFromPath(availableBooks.get(bookIndex));
String author = BookFileHelper.getBookAuthorFromPath(availableBooks.get(bookIndex));
String niceName = color(title) + color(Formatting.NEUTRAL_BOOK_LIST_AUTHOR_SEPARATOR) + color(author);
componentBuilder.append(niceName).color(ChatColor.WHITE).event(
new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + command + " " + new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + command + " " +
availableBooks.get(bookIndex))).event( availableBooks.get(bookIndex))).event(
new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Select book by path"))); new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(BooksWithoutBorders.getStringFormatter().
getUnFormattedColoredMessage(Formatting.NEUTRAL_AUTHOR_BOOKS_PATH))));
componentBuilder.append("\n"); componentBuilder.append("\n");
} }
} }

View File

@@ -1,7 +1,12 @@
package net.knarcraft.bookswithoutborders.gui; package net.knarcraft.bookswithoutborders.gui;
import net.knarcraft.bookswithoutborders.utility.BookFileHelper; import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.translation.Formatting;
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper; import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper;
import net.knarcraft.knarlib.formatting.StringFormatter;
import net.knarcraft.knarlib.formatting.TranslatableMessage;
import net.knarcraft.knarlib.property.ColorConversion;
import net.knarcraft.knarlib.util.ColorHelper;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder; import net.md_5.bungee.api.chat.ComponentBuilder;
@@ -9,7 +14,9 @@ import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.hover.content.Text; import net.md_5.bungee.api.chat.hover.content.Text;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map; import java.util.Map;
public abstract class BookIndex { public abstract class BookIndex {
@@ -80,8 +87,11 @@ public abstract class BookIndex {
* @param page <p>The current page</p> * @param page <p>The current page</p>
* @param totalPages <p>The total amount of pages</p> * @param totalPages <p>The total amount of pages</p>
*/ */
protected static void displayTotalPages(@NotNull ComponentBuilder componentBuilder, @NotNull String command, int page, int totalPages) { protected static void displayTotalPages(@NotNull ComponentBuilder componentBuilder, @NotNull String command,
componentBuilder.append("Page " + page + " of " + totalPages, int page, int totalPages) {
String pageDisplay = color(Formatting.NEUTRAL_BOOK_LIST_TOTAL_PAGES, List.of("{current}", "{total}"),
List.of(String.valueOf(page), String.valueOf(totalPages)));
componentBuilder.append(pageDisplay,
ComponentBuilder.FormatRetention.NONE).color(interactColor).event(new HoverEvent( ComponentBuilder.FormatRetention.NONE).color(interactColor).event(new HoverEvent(
HoverEvent.Action.SHOW_TEXT, new Text("/" + command + " page" + page))).event( HoverEvent.Action.SHOW_TEXT, new Text("/" + command + " page" + page))).event(
new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + command + " page" + page)); new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + command + " page" + page));
@@ -101,10 +111,12 @@ public abstract class BookIndex {
char character = (char) ('a' + characterIndex); char character = (char) ('a' + characterIndex);
if (firstInstances.containsKey(character)) { if (firstInstances.containsKey(character)) {
int pageIndex = (firstInstances.get(character) / booksPerPage) + 1; int pageIndex = (firstInstances.get(character) / booksPerPage) + 1;
componentBuilder.append(character + "").color(interactColor).event( HoverEvent hoverEvent = new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(
new ClickEvent(ClickEvent.Action.RUN_COMMAND, color(Formatting.NEUTRAL_BOOK_LIST_INDEX_HOVER, "{character}", "" + character)));
"/" + command + " page" + pageIndex)).event(new HoverEvent( ClickEvent clickEvent = new ClickEvent(ClickEvent.Action.RUN_COMMAND,
HoverEvent.Action.SHOW_TEXT, new Text("Books starting with " + character))); "/" + command + " page" + pageIndex);
componentBuilder.append(character + "").color(interactColor).event(clickEvent).event(hoverEvent);
} else { } else {
componentBuilder.append(character + "", ComponentBuilder.FormatRetention.NONE).color(inactiveColor); componentBuilder.append(character + "", ComponentBuilder.FormatRetention.NONE).color(inactiveColor);
} }
@@ -121,14 +133,16 @@ public abstract class BookIndex {
*/ */
protected static void displayPreviousButton(@NotNull ComponentBuilder componentBuilder, protected static void displayPreviousButton(@NotNull ComponentBuilder componentBuilder,
@NotNull String command, int page) { @NotNull String command, int page) {
String previousPage = color(Formatting.NEUTRAL_BOOK_LIST_PREVIOUS_PAGE);
if (page > 1) { if (page > 1) {
String fullCommand = "/" + command + " page" + (page - 1); String fullCommand = "/" + command + " page" + (page - 1);
HoverEvent prevPagePreview = new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("To page " + (page - 1))); HoverEvent prevPagePreview = new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(
color(Formatting.NEUTRAL_BOOK_LIST_TO_PAGE, "{page}", String.valueOf(page - 1))));
ClickEvent prevPageClick = new ClickEvent(ClickEvent.Action.RUN_COMMAND, fullCommand); ClickEvent prevPageClick = new ClickEvent(ClickEvent.Action.RUN_COMMAND, fullCommand);
componentBuilder.append("[<] Previous", ComponentBuilder.FormatRetention.NONE).color(interactColor) componentBuilder.append(previousPage, ComponentBuilder.FormatRetention.NONE).color(interactColor).
.event(prevPagePreview).event(prevPageClick); event(prevPagePreview).event(prevPageClick);
} else { } else {
componentBuilder.append("[<] Previous", ComponentBuilder.FormatRetention.NONE).color(inactiveColor); componentBuilder.append(previousPage, ComponentBuilder.FormatRetention.NONE).color(inactiveColor);
} }
} }
@@ -142,29 +156,73 @@ public abstract class BookIndex {
*/ */
protected static void displayNextButton(@NotNull ComponentBuilder componentBuilder, protected static void displayNextButton(@NotNull ComponentBuilder componentBuilder,
@NotNull String command, int page, int totalPages) { @NotNull String command, int page, int totalPages) {
String nextPage = color(Formatting.NEUTRAL_BOOK_LIST_NEXT_PAGE);
if (page < totalPages) { if (page < totalPages) {
String fullCommand = "/" + command + " page" + (page + 1); String fullCommand = "/" + command + " page" + (page + 1);
HoverEvent nextPagePreview = new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("To page " + (page + 1))); HoverEvent nextPagePreview = new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(
color(Formatting.NEUTRAL_BOOK_LIST_TO_PAGE, "{page}", String.valueOf(page + 1))));
ClickEvent nextPageClick = new ClickEvent(ClickEvent.Action.RUN_COMMAND, fullCommand); ClickEvent nextPageClick = new ClickEvent(ClickEvent.Action.RUN_COMMAND, fullCommand);
componentBuilder.append("Next [>]", ComponentBuilder.FormatRetention.NONE).color(interactColor) componentBuilder.append(nextPage, ComponentBuilder.FormatRetention.NONE).color(interactColor)
.event(nextPagePreview).event(nextPageClick); .event(nextPagePreview).event(nextPageClick);
} else { } else {
componentBuilder.append("Next [>]", ComponentBuilder.FormatRetention.NONE).color(inactiveColor); componentBuilder.append(nextPage, ComponentBuilder.FormatRetention.NONE).color(inactiveColor);
} }
} }
/** /**
* Gets a nice name from a book's path * Colors a translatable message
* *
* @param bookPath <p>The path of a book</p> * @param translatableMessage <p>The message to color</p>
* @return <p>The prettified book name</p> * @return <p>The colored message</p>
*/ */
@NotNull @NotNull
protected static String getNiceName(@NotNull String bookPath) { protected static String color(@NotNull TranslatableMessage translatableMessage) {
String title = BookFileHelper.getBookTitleFromPath(bookPath); return color(translatableMessage, (List<String>) null, null);
String author = BookFileHelper.getBookAuthorFromPath(bookPath); }
return ChatColor.translateAlternateColorCodes('&',
title + ChatColor.RESET + " by " + author + ChatColor.RESET); /**
* Colors a translatable message
*
* @param translatableMessage <p>The message to color</p>
* @param placeholder <p>Placeholder to replace</p>
* @param replacement <p>Replacement value</p>
* @return <p>The colored message</p>
*/
@NotNull
protected static String color(@NotNull TranslatableMessage translatableMessage, @NotNull String placeholder,
@NotNull String replacement) {
return color(translatableMessage, List.of(placeholder), List.of(replacement));
}
/**
* Colors a translatable message
*
* @param translatableMessage <p>The message to color</p>
* @param placeholders <p>Placeholders to replace</p>
* @param replacements <p>Replacement values</p>
* @return <p>The colored message</p>
*/
@NotNull
protected static String color(@NotNull TranslatableMessage translatableMessage, @Nullable List<String> placeholders,
@Nullable List<String> replacements) {
StringFormatter stringFormatter = BooksWithoutBorders.getStringFormatter();
if (placeholders == null || replacements == null) {
return stringFormatter.getUnFormattedColoredMessage(translatableMessage);
} else {
return ColorHelper.translateColorCodes(stringFormatter.replacePlaceholders(translatableMessage,
placeholders, replacements), ColorConversion.RGB);
}
}
/**
* Colors a message
*
* @param input <p>The message to color</p>
* @return <p>The colored message</p>
*/
@NotNull
protected static String color(@NotNull String input) {
return ColorHelper.translateColorCodes(input, ColorConversion.RGB);
} }
} }

View File

@@ -1,7 +1,10 @@
package net.knarcraft.bookswithoutborders.gui; package net.knarcraft.bookswithoutborders.gui;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders; import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.translation.Formatting;
import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
import net.knarcraft.bookswithoutborders.utility.BookFormatter; import net.knarcraft.bookswithoutborders.utility.BookFormatter;
import net.knarcraft.knarlib.formatting.TranslatableMessage;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder; import net.md_5.bungee.api.chat.ComponentBuilder;
@@ -42,9 +45,9 @@ public class PagedBookIndex extends BookIndex {
int totalPages = (int) Math.ceil((double) availableBooks.size() / booksPerPage); int totalPages = (int) Math.ceil((double) availableBooks.size() / booksPerPage);
if (page > totalPages) { if (page > totalPages) {
sender.sendMessage(ChatColor.GRAY + "No such page"); BooksWithoutBorders.getStringFormatter().displayErrorMessage(sender, Formatting.NEUTRAL_AUTHOR_BOOKS_INVALID_PAGE);
} else { } else {
showBookMenu(sender, command, page, totalPages, availableBooks, firstInstances); showBookMenu(sender, command, page, totalPages, availableBooks, firstInstances, listPublic);
} }
} }
@@ -57,19 +60,15 @@ public class PagedBookIndex extends BookIndex {
* @param totalPages <p>The total amount of pages</p> * @param totalPages <p>The total amount of pages</p>
* @param availableBooks <p>All books available to the sender</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> * @param firstInstances <p>The map between a character, and the index of the first instance of that character in the book list</p>
* @param listPublic <p>Whether to display public books</p>
*/ */
private static void showBookMenu(@NotNull CommandSender sender, @NotNull String command, int page, private static void showBookMenu(@NotNull CommandSender sender, @NotNull String command, int page,
int totalPages, @NotNull List<String> availableBooks, int totalPages, @NotNull List<String> availableBooks,
@NotNull Map<Character, Integer> firstInstances) { @NotNull Map<Character, Integer> firstInstances, boolean listPublic) {
ComponentBuilder componentBuilder = new ComponentBuilder(); ComponentBuilder componentBuilder = new ComponentBuilder();
componentBuilder.append("--- "); TranslatableMessage message = listPublic ? Formatting.NEUTRAL_BOOK_LIST_PUBLIC_BOOKS_HEADER : Formatting.NEUTRAL_BOOK_LIST_PLAYER_BOOKS_HEADER;
if (command.toLowerCase().contains("public")) { componentBuilder.append(color(message));
componentBuilder.append("Publicly saved books").color(ChatColor.GREEN);
} else {
componentBuilder.append("Your saved books").color(ChatColor.GREEN);
}
componentBuilder.append(" ---", ComponentBuilder.FormatRetention.NONE).append("\n");
displayBookList(componentBuilder, command, page, availableBooks); displayBookList(componentBuilder, command, page, availableBooks);
displayPreviousButton(componentBuilder, command, page); displayPreviousButton(componentBuilder, command, page);
@@ -94,24 +93,23 @@ public class PagedBookIndex extends BookIndex {
@NotNull List<String> availableBooks) { @NotNull List<String> availableBooks) {
int startIndex = (page - 1) * booksPerPage; int startIndex = (page - 1) * booksPerPage;
for (int bookIndex = startIndex; bookIndex < Math.min(startIndex + booksPerPage, availableBooks.size()); bookIndex++) { for (int bookIndex = startIndex; bookIndex < Math.min(startIndex + booksPerPage, availableBooks.size()); bookIndex++) {
componentBuilder.append("[" + (bookIndex + 1) + "]").color(interactColor).event( String title = color(BookFileHelper.getBookTitleFromPath(availableBooks.get(bookIndex)));
new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + command + " " + String author = color(BookFileHelper.getBookAuthorFromPath(availableBooks.get(bookIndex)));
(bookIndex + 1))).event(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
new Text("Select book by index")));
ClickEvent indexClick = new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + command + " " + (bookIndex + 1));
HoverEvent indexHover = new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(color(Formatting.NEUTRAL_BOOK_LIST_BOOK_INDEX_HOVER)));
ClickEvent pathClick = new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + command + " " + availableBooks.get(bookIndex));
HoverEvent pathHover = new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(color(Formatting.NEUTRAL_BOOK_LIST_PATH_HOVER)));
ClickEvent authorClick = new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/" + command + " author" + BookFormatter.stripColor(author) + " page1");
HoverEvent authorHover = new HoverEvent(HoverEvent.Action.SHOW_TEXT,
new Text(color(Formatting.NEUTRAL_BOOK_LIST_AUTHOR_HOVER, "{author}", BookFormatter.stripColor(author))));
componentBuilder.append(color(Formatting.NEUTRAL_BOOK_LIST_BOOK_INDEX_NUMBER, "{index}",
String.valueOf(bookIndex + 1))).color(interactColor).event(indexClick).event(indexHover);
componentBuilder.append(" ", ComponentBuilder.FormatRetention.NONE); componentBuilder.append(" ", ComponentBuilder.FormatRetention.NONE);
componentBuilder.append(title).color(ChatColor.WHITE).event(pathClick).event(pathHover);
String[] parts = getNiceName(availableBooks.get(bookIndex)).split(" by "); componentBuilder.append(color(Formatting.NEUTRAL_BOOK_LIST_AUTHOR_SEPARATOR), ComponentBuilder.FormatRetention.NONE).color(ChatColor.WHITE);
componentBuilder.append(parts[0]).color(ChatColor.WHITE).event( componentBuilder.append(author).color(ChatColor.WHITE).event(authorClick).event(authorHover);
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"); componentBuilder.append("\n");
} }
} }

View File

@@ -3,6 +3,7 @@ package net.knarcraft.bookswithoutborders.utility;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders; import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.state.BookHoldingState; import net.knarcraft.bookswithoutborders.state.BookHoldingState;
import net.knarcraft.bookswithoutborders.state.ItemSlot; import net.knarcraft.bookswithoutborders.state.ItemSlot;
import net.knarcraft.knarlib.formatting.StringFormatter;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
@@ -79,16 +80,17 @@ public final class InventoryHelper {
*/ */
public static boolean notHoldingOneWritableBookCheck(@NotNull Player player, @NotNull String noBookMessage, public static boolean notHoldingOneWritableBookCheck(@NotNull Player player, @NotNull String noBookMessage,
@NotNull String twoBooksMessage) { @NotNull String twoBooksMessage) {
StringFormatter stringFormatter = BooksWithoutBorders.getStringFormatter();
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 ||
holdingState == BookHoldingState.SIGNED_MAIN_HAND || holdingState == BookHoldingState.SIGNED_OFF_HAND) { holdingState == BookHoldingState.SIGNED_MAIN_HAND || holdingState == BookHoldingState.SIGNED_OFF_HAND) {
BooksWithoutBorders.sendErrorMessage(player, noBookMessage); stringFormatter.displayErrorMessage(player, noBookMessage);
return true; return true;
} }
if (holdingState == BookHoldingState.UNSIGNED_BOTH_HANDS) { if (holdingState == BookHoldingState.UNSIGNED_BOTH_HANDS) {
BooksWithoutBorders.sendErrorMessage(player, twoBooksMessage); stringFormatter.displayErrorMessage(player, twoBooksMessage);
return true; return true;
} }

View File

@@ -23,6 +23,7 @@ en:
SUCCESS_BOOKSHELF_LORE_SET: "Lore successfully saved!" SUCCESS_BOOKSHELF_LORE_SET: "Lore successfully saved!"
SUCCESS_GENERATION_CHANGED: "Book generation successfully changed!" SUCCESS_GENERATION_CHANGED: "Book generation successfully changed!"
SUCCESS_LORE_SET: "Added lore to item!" SUCCESS_LORE_SET: "Added lore to item!"
SUCCESS_TITLE_SET: "Title set to {title}!"
ACTION_COPY: "copy" ACTION_COPY: "copy"
ACTION_CLEAR: "clear" ACTION_CLEAR: "clear"
ACTION_DECRYPT: "decrypt" ACTION_DECRYPT: "decrypt"
@@ -30,6 +31,7 @@ en:
ACTION_ADD_TITLE_AUTHOR_PAGE: "add an author title page to" ACTION_ADD_TITLE_AUTHOR_PAGE: "add an author title page to"
ACTION_SET_AUTHOR: "set author" ACTION_SET_AUTHOR: "set author"
ACTION_CHANGE_GENERATION: "change generation" ACTION_CHANGE_GENERATION: "change generation"
ACTION_UNSIGN: "unsign"
ERROR_PLAYER_ONLY: "This command can only be used by a player!" ERROR_PLAYER_ONLY: "This command can only be used by a player!"
ERROR_NOT_HOLDING_WRITTEN_BOOK: "You must be holding a written book to {action} it!" ERROR_NOT_HOLDING_WRITTEN_BOOK: "You must be holding a written book to {action} it!"
ERROR_NOT_HOLDING_WRITABLE_BOOK: "You must be holding a writable book to {action} it!" ERROR_NOT_HOLDING_WRITABLE_BOOK: "You must be holding a writable book to {action} it!"
@@ -86,8 +88,12 @@ en:
ERROR_GENERATION_NOT_SPECIFIED: "You must specify the new generation for your book!" ERROR_GENERATION_NOT_SPECIFIED: "You must specify the new generation for your book!"
ERROR_GENERATION_INVALID: "Invalid book generation specified!" ERROR_GENERATION_INVALID: "Invalid book generation specified!"
ERROR_LORE_EMPTY: "You must specify the new lore to set!" ERROR_LORE_EMPTY: "You must specify the new lore to set!"
ERROR_LORE_NO_ITEM: "Must be holding an item to set lore!" ERROR_NO_ITEM: "You must be holding an item to use this command!"
ERROR_TITLE_EMPTY: "You must specify the new title/display name to set!" ERROR_TITLE_EMPTY: "You must specify the new title/display name to set!"
ERROR_TITLE_LENGTH: "Book titles are capped at 32 characters!"
# ---------------------------------- #
# Custom formatting for some output #
# ---------------------------------- #
NEUTRAL_COMMANDS_HEADER: | NEUTRAL_COMMANDS_HEADER: |
&nBooks without Borders help page&r &nBooks without Borders help page&r
&e[] = optional, <> = required (see each description for exceptions) &e[] = optional, <> = required (see each description for exceptions)
@@ -100,8 +106,28 @@ en:
NEUTRAL_COMMANDS_COMMAND_NO_PERMISSION_REQUIRED: "None" NEUTRAL_COMMANDS_COMMAND_NO_PERMISSION_REQUIRED: "None"
NEUTRAL_COMMANDS_COMMAND_PERMISSION: " &7{{permission}}" NEUTRAL_COMMANDS_COMMAND_PERMISSION: " &7{{permission}}"
NEUTRAL_COMMANDS_ALIASES: " &f(&b{aliases}&f)" NEUTRAL_COMMANDS_ALIASES: " &f(&b{aliases}&f)"
NEUTRAL_UNKNOWN_AUTHOR: "Unknown"
NEUTRAL_UNKNOWN_TITLE: "Untitled"
NEUTRAL_TITLE_PAGE_TITLE_AUTHOR_FORMAT: "{title}{separator}By: {author}" NEUTRAL_TITLE_PAGE_TITLE_AUTHOR_FORMAT: "{title}{separator}By: {author}"
NEUTRAL_TITLE_PAGE_HEADER_FORMAT: "\n&n&l{header}&r" NEUTRAL_TITLE_PAGE_HEADER_FORMAT: "\n&n&l{header}&r"
NEUTRAL_TITLE_PAGE_TEXT_FORMAT: "\n\n&o{text}&r" NEUTRAL_TITLE_PAGE_TEXT_FORMAT: "\n\n&o{text}&r"
NEUTRAL_AUTHOR_PUBLIC_BOOKS_HEADER: "--- &aPublicly saved books by: &b{author}&r ---\n"
NEUTRAL_AUTHOR_PLAYER_BOOKS_HEADER: "--- &aYour saved books by: &b{author}&r ---\n"
NEUTRAL_AUTHOR_BOOKS_PATH: "Select book by path"
NEUTRAL_AUTHOR_BOOKS_INVALID_PAGE: "&7No such page"
NEUTRAL_BOOK_LIST_TOTAL_PAGES: "Page {current} of {total}"
NEUTRAL_BOOK_LIST_PREVIOUS_PAGE: "[<] Previous"
NEUTRAL_BOOK_LIST_NEXT_PAGE: "Next [>]"
NEUTRAL_BOOK_LIST_AUTHOR_SEPARATOR: "&r by "
NEUTRAL_BOOK_LIST_TO_PAGE: "To page {page}"
NEUTRAL_BOOK_LIST_INDEX_HOVER: "Books starting with {character}"
NEUTRAL_BOOK_LIST_PUBLIC_BOOKS_HEADER: "--- &aPublicly saved books&r ---\n"
NEUTRAL_BOOK_LIST_PLAYER_BOOKS_HEADER: "--- &aYour saved books&r ---\n"
NEUTRAL_BOOK_LIST_AUTHOR_HOVER: "Books by {author}"
NEUTRAL_BOOK_LIST_PATH_HOVER: "Select book by path"
NEUTRAL_BOOK_LIST_BOOK_INDEX_HOVER: "Select book by index"
NEUTRAL_BOOK_LIST_BOOK_INDEX_NUMBER: "[{index}]"
# -----------------------------------------#
# Translations of unknown/untitled author. #
# Altering this might cause problems. #
# -----------------------------------------#
NEUTRAL_UNKNOWN_AUTHOR: "Unknown"
NEUTRAL_UNKNOWN_TITLE: "Untitled"