Replaces lots more static strings
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good

This commit is contained in:
2025-08-21 01:09:41 +02:00
parent b01523f058
commit 9641852f82
15 changed files with 472 additions and 201 deletions

View File

@@ -34,6 +34,7 @@ import net.knarcraft.bookswithoutborders.config.translation.CostMessage;
import net.knarcraft.bookswithoutborders.config.translation.Formatting;
import net.knarcraft.bookswithoutborders.config.translation.GiveMessage;
import net.knarcraft.bookswithoutborders.config.translation.SaveMessage;
import net.knarcraft.bookswithoutborders.config.translation.SignText;
import net.knarcraft.bookswithoutborders.config.translation.Translatable;
import net.knarcraft.bookswithoutborders.container.MigrationRequest;
import net.knarcraft.bookswithoutborders.handler.BookshelfHandler;
@@ -217,6 +218,7 @@ public class BooksWithoutBorders extends JavaPlugin {
translator.registerMessageCategory(Formatting.NEUTRAL_COMMANDS_HEADER);
translator.registerMessageCategory(CostMessage.SUCCESS_COST_ITEM_SET);
translator.registerMessageCategory(SaveMessage.SUCCESS_SAVED);
translator.registerMessageCategory(SignText.SIGN_HEADER);
stringFormatter = new StringFormatter(this.getDescription().getName(), translator);
stringFormatter.setColorConversion(ColorConversion.RGB);

View File

@@ -56,65 +56,13 @@ public class CommandGive implements TabExecutor {
return true;
}
int argumentCount = arguments.length;
StringFormatter stringFormatter = BooksWithoutBorders.getStringFormatter();
if (arguments.length == 1) {
stringFormatter.displayErrorMessage(sender, GiveMessage.ERROR_GIVE_NO_RECIPIENT);
return false;
}
//Organize and parse input
String bookIdentifier;
String receivingPlayerName;
String copies = "1";
String isSigned = "true";
if (argumentCount > 3 && InputCleaningHelper.isInt(arguments[argumentCount - 2]) &&
InputCleaningHelper.isBoolean(arguments[argumentCount - 1])) {
receivingPlayerName = arguments[argumentCount - 3];
isSigned = arguments[argumentCount - 1];
copies = arguments[argumentCount - 2];
bookIdentifier = InputCleaningHelper.mergeArguments(arguments, 3);
} else if (argumentCount > 2 && InputCleaningHelper.isBoolean(arguments[argumentCount - 1])) {
isSigned = arguments[argumentCount - 1];
receivingPlayerName = arguments[argumentCount - 2];
bookIdentifier = InputCleaningHelper.mergeArguments(arguments, 2);
} else if (argumentCount > 2 && InputCleaningHelper.isInt(arguments[argumentCount - 1])) {
copies = arguments[argumentCount - 1];
receivingPlayerName = arguments[argumentCount - 2];
bookIdentifier = InputCleaningHelper.mergeArguments(arguments, 2);
} else {
receivingPlayerName = arguments[argumentCount - 1];
bookIdentifier = InputCleaningHelper.mergeArguments(arguments, 1);
}
//Try and find the target player
Player receivingPlayer = booksWithoutBorders.getServer().getPlayerExact(receivingPlayerName);
if (receivingPlayer == null) {
stringFormatter.displayErrorMessage(sender, GiveMessage.ERROR_GIVE_RECIPIENT_UNKNOWN);
return false;
}
//Make sure the receiver is able to fit the book
if (receivingPlayer.getInventory().firstEmpty() == -1) {
stringFormatter.displayErrorMessage(sender, GiveMessage.ERROR_GIVE_RECIPIENT_FULL);
return false;
}
//Load books available to the player
try {
Integer.parseInt(bookIdentifier);
BooksWithoutBorders.updateBooks(sender, givePublic);
} catch (NumberFormatException ignored) {
}
try {
return loadAndGiveBook(bookIdentifier, sender, receivingPlayer, isSigned, folder, copies);
} catch (NumberFormatException e) {
stringFormatter.displayErrorMessage(sender, Translatable.ERROR_INVALID_COPIES_AMOUNT);
return false;
}
return parseArgumentsAndContinue(arguments, sender, folder, givePublic);
}
@Override
@@ -172,6 +120,64 @@ public class CommandGive implements TabExecutor {
return output;
}
/**
* Parses the user input, before continuing to load the book
*
* @param arguments <p>The arguments given by the player</p>
* @param sender <p>The player attempting to load a book</p>
* @param directory <p>The directory to load from</p>
* @param givePublic <p>Whether to give from the public or player directory</p>
* @return <p>True if successful, false otherwise</p>
* @throws NumberFormatException <p>If the number of copies given is not a number</p>
*/
private boolean parseArgumentsAndContinue(@NotNull String[] arguments, @NotNull CommandSender sender,
@NotNull String directory, boolean givePublic) {
StringFormatter stringFormatter = BooksWithoutBorders.getStringFormatter();
int argumentCount = arguments.length;
//Organize and parse input
String bookIdentifier;
String receivingPlayerName;
String copies = "1";
boolean isSigned = true;
// Parse arguments at the end of the command, and treat the rest as the book name
if (argumentCount > 3 && InputCleaningHelper.isInt(arguments[argumentCount - 2]) &&
InputCleaningHelper.isBoolean(arguments[argumentCount - 1])) {
receivingPlayerName = arguments[argumentCount - 3];
isSigned = Boolean.parseBoolean(arguments[argumentCount - 1]);
copies = arguments[argumentCount - 2];
bookIdentifier = InputCleaningHelper.mergeArguments(arguments, 3);
} else if (argumentCount > 2 && InputCleaningHelper.isBoolean(arguments[argumentCount - 1])) {
isSigned = Boolean.parseBoolean(arguments[argumentCount - 1]);
receivingPlayerName = arguments[argumentCount - 2];
bookIdentifier = InputCleaningHelper.mergeArguments(arguments, 2);
} else if (argumentCount > 2 && InputCleaningHelper.isInt(arguments[argumentCount - 1])) {
copies = arguments[argumentCount - 1];
receivingPlayerName = arguments[argumentCount - 2];
bookIdentifier = InputCleaningHelper.mergeArguments(arguments, 2);
} else {
receivingPlayerName = arguments[argumentCount - 1];
bookIdentifier = InputCleaningHelper.mergeArguments(arguments, 1);
}
//Try and find the target player
Player receivingPlayer = booksWithoutBorders.getServer().getPlayerExact(receivingPlayerName);
if (receivingPlayer == null) {
stringFormatter.displayErrorMessage(sender, GiveMessage.ERROR_GIVE_RECIPIENT_UNKNOWN);
return false;
}
//Make sure the receiver is able to fit the book
if (receivingPlayer.getInventory().firstEmpty() == -1) {
stringFormatter.displayErrorMessage(sender, GiveMessage.ERROR_GIVE_RECIPIENT_FULL);
return false;
}
//Load books available to the player
BooksWithoutBorders.updateBooks(sender, givePublic);
return loadAndGiveBook(bookIdentifier, sender, receivingPlayer, isSigned, directory, copies);
}
/**
* Loads a book and gives it to the correct player
*
@@ -184,7 +190,7 @@ public class CommandGive implements TabExecutor {
* @return <p>True if the book was successfully given</p>
*/
private boolean loadAndGiveBook(@NotNull String bookIdentifier, @NotNull CommandSender sender,
@NotNull Player receivingPlayer, @NotNull String isSigned, @NotNull String folder,
@NotNull Player receivingPlayer, boolean isSigned, @NotNull String folder,
@NotNull String copies) throws NumberFormatException {
StringFormatter stringFormatter = BooksWithoutBorders.getStringFormatter();
String bookToLoad = InputCleaningHelper.cleanString(bookIdentifier);

View File

@@ -52,58 +52,12 @@ public class CommandLoad implements TabExecutor {
return false;
}
int argumentCount = arguments.length;
if (PagedBookIndex.displayPage(arguments, sender, loadPublic, commandName)) {
return true;
}
//Organize and parse input
String bookIdentifier = arguments[0];
String copies = "1";
String isSigned = "true";
// Parse arguments at the end of the command, and treat the rest as the book name
if (argumentCount > 1) {
if (argumentCount > 2 && InputCleaningHelper.isInt(arguments[argumentCount - 2]) &&
InputCleaningHelper.isBoolean(arguments[argumentCount - 1])) {
isSigned = arguments[argumentCount - 1];
copies = arguments[argumentCount - 2];
bookIdentifier = InputCleaningHelper.mergeArguments(arguments, 2);
} else if (InputCleaningHelper.isBoolean(arguments[argumentCount - 1])) {
isSigned = arguments[argumentCount - 1];
bookIdentifier = InputCleaningHelper.mergeArguments(arguments, 1);
} else if (InputCleaningHelper.isInt(arguments[argumentCount - 1])) {
copies = arguments[argumentCount - 1];
bookIdentifier = InputCleaningHelper.mergeArguments(arguments, 1);
} else {
bookIdentifier = InputCleaningHelper.mergeArguments(arguments, 0);
}
}
//Load books available to the player
try {
Integer.parseInt(bookIdentifier);
BooksWithoutBorders.updateBooks(sender, loadPublic);
} catch (NumberFormatException ignored) {
}
String bookToLoad = InputCleaningHelper.cleanString(bookIdentifier);
try {
//Give the new book if it can be loaded
ItemStack newBook = BookLoader.loadBook(player, bookToLoad, isSigned, directory, Integer.parseInt(copies));
if (newBook != null) {
player.getInventory().addItem(newBook);
stringFormatter.displaySuccessMessage(player, Translatable.SUCCESS_BOOK_LOADED);
return true;
} else {
stringFormatter.displayErrorMessage(player, Translatable.ERROR_LOAD_FAILED);
return false;
}
} catch (NumberFormatException e) {
stringFormatter.displayErrorMessage(player, Translatable.ERROR_INVALID_COPIES_AMOUNT);
return false;
}
return parseArgumentsAndContinue(arguments, player, directory, loadPublic);
}
@Override
@@ -151,4 +105,72 @@ public class CommandLoad implements TabExecutor {
return output;
}
/**
* Parses the user input, before continuing to load the book
*
* @param arguments <p>The arguments given by the player</p>
* @param player <p>The player attempting to load a book</p>
* @param directory <p>The directory to load from</p>
* @param loadPublic <p>Whether to load from the public or player directory</p>
* @return <p>True if successful, false otherwise</p>
* @throws NumberFormatException <p>If the number of copies given is not a number</p>
*/
private boolean parseArgumentsAndContinue(@NotNull String[] arguments, @NotNull Player player,
@NotNull String directory, boolean loadPublic) {
int argumentCount = arguments.length;
String bookIdentifier = arguments[0];
int copies = 1;
boolean isSigned = true;
// Parse arguments at the end of the command, and treat the rest as the book name
if (argumentCount > 1) {
if (argumentCount > 2 && InputCleaningHelper.isInt(arguments[argumentCount - 2]) &&
InputCleaningHelper.isBoolean(arguments[argumentCount - 1])) {
isSigned = Boolean.parseBoolean(arguments[argumentCount - 1]);
copies = Integer.parseInt(arguments[argumentCount - 2]);
bookIdentifier = InputCleaningHelper.mergeArguments(arguments, 2);
} else if (InputCleaningHelper.isBoolean(arguments[argumentCount - 1])) {
isSigned = Boolean.parseBoolean(arguments[argumentCount - 1]);
bookIdentifier = InputCleaningHelper.mergeArguments(arguments, 1);
} else if (InputCleaningHelper.isInt(arguments[argumentCount - 1])) {
copies = Integer.parseInt(arguments[argumentCount - 1]);
bookIdentifier = InputCleaningHelper.mergeArguments(arguments, 1);
} else {
bookIdentifier = InputCleaningHelper.mergeArguments(arguments, 0);
}
}
return loadBook(player, loadPublic, bookIdentifier, isSigned, directory, copies);
}
/**
* Loads the specified book
*
* @param player <p>The player attempting to load the book</p>
* @param loadPublic <p>Whether to load a book from the public directory</p>
* @param bookIdentifier <p>The identifier of the book to load</p>
* @param isSigned <p>Whether the loaded book should be signed</p>
* @param directory <p>The directory to load the book from</p>
* @param copies <p>The number of copies to load</p>
* @return <p>The loaded book(s)</p>
*/
private boolean loadBook(@NotNull Player player, boolean loadPublic, @NotNull String bookIdentifier,
boolean isSigned, @NotNull String directory, int copies) {
StringFormatter stringFormatter = BooksWithoutBorders.getStringFormatter();
BooksWithoutBorders.updateBooks(player, loadPublic);
String bookToLoad = InputCleaningHelper.cleanString(bookIdentifier);
//Give the new book if it can be loaded
ItemStack newBook = BookLoader.loadBook(player, bookToLoad, isSigned, directory, copies);
if (newBook != null) {
player.getInventory().addItem(newBook);
stringFormatter.displaySuccessMessage(player, Translatable.SUCCESS_BOOK_LOADED);
return true;
} else {
stringFormatter.displayErrorMessage(player, Translatable.ERROR_LOAD_FAILED);
return false;
}
}
}

View File

@@ -41,7 +41,16 @@ public enum Permission {
* The permission for bypassing author only un-signing
*/
AUTHOR_ONLY_UNSIGN("bypassAuthorOnlyUnsign"),
;
/**
* The permission for peeking at a bookshelf's contents
*/
PEEK_BOOKSHELF("peekBookshelf"),
/**
* The permission for using special signs
*/
SIGNS("signs");
private final @NotNull String node;

View File

@@ -25,6 +25,7 @@ public enum StaticMessage {
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"),
WARNING_USER_BOOK_MIGRATION_IMPOSSIBLE("Unable to migrate player book directory for player {player}"),
;
private final @NotNull String messageString;

View File

@@ -147,6 +147,61 @@ public enum Formatting implements TranslatableMessage {
* The format used when showing book int index in the book list
*/
NEUTRAL_BOOK_LIST_BOOK_INDEX_NUMBER,
/**
* The format of the bookshelf header title
*/
NEUTRAL_BOOKSHELF_HEADER_TITLE,
/**
* The format of a bookshelf title which is not set
*/
NEUTRAL_BOOKSHELF_HEADER_TITLE_EMPTY,
/**
* The format of a bookshelf's lore line
*/
NEUTRAL_BOOKSHELF_HEADER_LORE,
/**
* The format of the sub-header for a bookshelf's top row
*/
NEUTRAL_BOOKSHELF_HEADER_TOP,
/**
* The format of the sub-header for a bookshelf's bottom row
*/
NEUTRAL_BOOKSHELF_HEADER_BOTTOM,
/**
* The format of the prefix used when displaying an enchanted book in a bookcase
*/
NEUTRAL_BOOKSHELF_ENCHANTED_PREFIX,
/**
* The format used when displaying a player written book in a bookshelf
*/
NEUTRAL_BOOKSHELF_WRITTEN_FORMAT,
/**
* The format of the prefix used when displaying a plain book in a bookcase
*/
NEUTRAL_BOOKSHELF_PLAIN_BOOK_PREFIX,
/**
* The format used when displaying an unnamed plain book in a bookshelf
*/
NEUTRAL_BOOKSHELF_UNNAMED_PLAIN_BOOK_FORMAT,
/**
* The format used when displaying the index of a book in a bookshelf
*/
NEUTRAL_BOOKSHELF_ENTRY_INDEX,
/**
* The format used when displaying that a bookshelf position is empty
*/
NEUTRAL_BOOKSHELF_EMPTY,
;
@Override

View File

@@ -0,0 +1,62 @@
package net.knarcraft.bookswithoutborders.config.translation;
import net.knarcraft.knarlib.formatting.TranslatableMessage;
import org.jetbrains.annotations.NotNull;
/**
* Translations for the text on BwB signs
*/
public enum SignText implements TranslatableMessage {
/**
* The header for all BwB signs
*/
SIGN_HEADER,
/**
* The encryption sign specifier
*/
SIGN_ENCRYPT,
/**
* The decryption sign specifier
*/
SIGN_DECRYPT,
/**
* The give sign specifier
*/
SIGN_GIVE,
/**
* The format for displaying the password on the sign
*/
SIGN_PASSWORD,
/**
* The format for marking a sign line as invalid
*/
SIGN_INVALID,
/**
* The format for marking a sign line as valid
*/
SIGN_VALID,
/**
* The error displayed when a player creates an invalid BwB sign
*/
ERROR_SIGN_INVALID,
/**
* The error displayed when attempting to use an invalid BwB sign
*/
ERROR_SIGN_COMMAND_INVALID,
;
@Override
public @NotNull TranslatableMessage[] getAllMessages() {
return SignText.values();
}
}

View File

@@ -228,11 +228,6 @@ public enum Translatable implements TranslatableMessage {
*/
ERROR_ENCRYPT_EMPTY,
/**
* The error displayed when using the give command with an invalid number of copies (unlikely to ever happen)
*/
ERROR_INVALID_COPIES_AMOUNT,
/**
* The error displayed when running a command without holding any book in the main hand
*/

View File

@@ -26,8 +26,12 @@ import java.util.logging.Level;
*/
public class BookshelfHandler {
private static final File bookshelfFile = new File(BooksWithoutBorders.getInstance().getDataFolder(),
private static final File BOOKSHELF_FILE = new File(BooksWithoutBorders.getInstance().getDataFolder(),
"bookshelves.yml");
private static final String BOOKSHELVES_SECTION = "bookshelves";
private static final String TITLE_KEY = ".title";
private static final String LORE_KEY = ".lore";
private Set<Bookshelf> bookshelves;
private Map<Location, Bookshelf> locationLookup;
@@ -69,8 +73,8 @@ public class BookshelfHandler {
this.bookshelves = new HashSet<>();
this.locationLookup = new HashMap<>();
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(bookshelfFile);
ConfigurationSection bookshelfSection = configuration.getConfigurationSection("bookshelves");
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(BOOKSHELF_FILE);
ConfigurationSection bookshelfSection = configuration.getConfigurationSection(BOOKSHELVES_SECTION);
if (bookshelfSection == null) {
BooksWithoutBorders.log(Level.INFO, StaticMessage.NOTICE_NO_BOOKSHELVES.toString());
return;
@@ -84,8 +88,8 @@ public class BookshelfHandler {
double z = Integer.parseInt(locationInfo[3]);
Location bookshelfLocation = new Location(world, x, y, z);
String titleKey = key + ".title";
String loreKey = key + ".lore";
String titleKey = key + TITLE_KEY;
String loreKey = key + LORE_KEY;
String title = bookshelfSection.getString(titleKey, null);
List<String> lore = bookshelfSection.getStringList(loreKey);
@@ -102,11 +106,11 @@ public class BookshelfHandler {
public void save() {
try {
YamlConfiguration configuration = new YamlConfiguration();
ConfigurationSection bookshelfSection = configuration.createSection("bookshelves");
ConfigurationSection bookshelfSection = configuration.createSection(BOOKSHELVES_SECTION);
for (Bookshelf bookshelf : bookshelves) {
saveBookshelf(bookshelfSection, bookshelf);
}
configuration.save(bookshelfFile);
configuration.save(BOOKSHELF_FILE);
} catch (IOException exception) {
BooksWithoutBorders.log(Level.SEVERE, StaticMessage.EXCEPTION_BOOKSHELF_SAVING_FAILED.toString());
}
@@ -124,10 +128,10 @@ public class BookshelfHandler {
return;
}
String key = location.getWorld().getUID() + "," + location.getBlockX() + "," + location.getBlockY() +
"," + location.getBlockZ();
String titleKey = key + ".title";
String loreKey = key + ".lore";
String key = location.getWorld().getUID() + "," + location.getBlockX() + "," + location.getBlockY() + "," +
location.getBlockZ();
String titleKey = key + TITLE_KEY;
String loreKey = key + LORE_KEY;
section.set(titleKey, bookshelf.getTitle());
section.set(loreKey, bookshelf.getLore());
}

View File

@@ -1,13 +1,13 @@
package net.knarcraft.bookswithoutborders.listener;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.Permission;
import net.knarcraft.bookswithoutborders.config.translation.Formatting;
import net.knarcraft.bookswithoutborders.container.Bookshelf;
import net.knarcraft.bookswithoutborders.handler.BookshelfHandler;
import net.knarcraft.bookswithoutborders.utility.BookHelper;
import net.knarcraft.bookswithoutborders.utility.IntegerToRomanConverter;
import net.knarcraft.knarlib.property.ColorConversion;
import net.knarcraft.knarlib.util.ColorHelper;
import net.md_5.bungee.api.ChatColor;
import net.knarcraft.knarlib.formatting.StringFormatter;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
@@ -66,7 +66,7 @@ public class BookshelfListener implements Listener {
// Check if bookshelf peeking is enabled, and the player can peek
if (!BooksWithoutBorders.getConfiguration().getEnableBookshelfPeeking() ||
!event.getPlayer().hasPermission("bookswithoutborders.peekbookshelf")) {
!event.getPlayer().hasPermission(Permission.PEEK_BOOKSHELF.toString())) {
return;
}
@@ -74,7 +74,8 @@ public class BookshelfListener implements Listener {
event.setUseItemInHand(Event.Result.DENY);
ChiseledBookshelfInventory bookshelfInventory = chiseledBookshelf.getInventory();
player.sendMessage(getBookshelfDescription(bookshelfInventory, event.getClickedBlock().getLocation()));
BooksWithoutBorders.getStringFormatter().displaySuccessMessage(player,
getBookshelfDescription(bookshelfInventory, event.getClickedBlock().getLocation()));
}
/**
@@ -86,48 +87,67 @@ public class BookshelfListener implements Listener {
*/
@NotNull
private String getBookshelfDescription(@NotNull ChiseledBookshelfInventory bookshelfInventory, @NotNull Location location) {
StringFormatter stringFormatter = BooksWithoutBorders.getStringFormatter();
StringBuilder builder = new StringBuilder();
Bookshelf bookshelf = BooksWithoutBorders.getBookshelfHandler().getFromLocation(location);
String title;
StringBuilder lore = new StringBuilder();
if (bookshelf != null) {
builder.append(ChatColor.of("#FF5700")).append("Books in ").append(
ColorHelper.translateColorCodes(bookshelf.getTitle(), ColorConversion.RGB)).append(
ChatColor.RESET).append(ChatColor.of("#FF5700")).append(":");
for (String lore : bookshelf.getLore()) {
builder.append("\n ").append(ChatColor.LIGHT_PURPLE).append(
ColorHelper.translateColorCodes(lore, ColorConversion.RGB)).append(ChatColor.RESET);
title = bookshelf.getTitle();
for (String loreLine : bookshelf.getLore()) {
lore.append(stringFormatter.replacePlaceholder(Formatting.NEUTRAL_BOOKSHELF_HEADER_LORE, "{lore}", loreLine));
}
} else {
builder.append(ChatColor.of("#FF5700")).append("Books in shelf:").append(ChatColor.RESET);
title = stringFormatter.getUnFormattedColoredMessage(Formatting.NEUTRAL_BOOKSHELF_HEADER_TITLE_EMPTY);
}
builder.append(stringFormatter.replacePlaceholders(Formatting.NEUTRAL_BOOKSHELF_HEADER_TITLE,
List.of("{name}", "{lore}"), List.of(title, lore.toString())));
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) {
builder.append(ChatColor.GRAY).append("<empty>");
continue;
}
ItemMeta meta = itemStack.getItemMeta();
if (meta instanceof BookMeta bookMeta) {
builder.append(getBookDescription(bookMeta));
} else if (meta instanceof EnchantmentStorageMeta enchantmentStorageMeta) {
builder.append(getEnchantedBookDescription(enchantmentStorageMeta));
} else if (meta != null) {
builder.append(ChatColor.of("#A5682A")).append("[P]").append(ChatColor.RESET).append(getPlainBookDescription(meta));
}
appendBookshelfItem(i, builder, bookshelfInventory);
}
return builder.toString();
}
/**
* Displays info about one item in a bookshelf
*
* @param counter <p>The book index to display</p>
* @param builder <p>The string builder to append to</p>
* @param bookshelfInventory <p>The inventory of the bookshelf to display a book from</p>
*/
private void appendBookshelfItem(int counter, @NotNull StringBuilder builder,
@NotNull ChiseledBookshelfInventory bookshelfInventory) {
StringFormatter stringFormatter = BooksWithoutBorders.getStringFormatter();
// Show the row header before the first item, and before the fourth item
int index = (counter % 3) + 1;
if (counter == 0) {
builder.append(stringFormatter.getUnFormattedColoredMessage(Formatting.NEUTRAL_BOOKSHELF_HEADER_TOP));
} else if (counter == 3) {
builder.append(stringFormatter.getUnFormattedColoredMessage(Formatting.NEUTRAL_BOOKSHELF_HEADER_BOTTOM));
}
builder.append(stringFormatter.replacePlaceholder(Formatting.NEUTRAL_BOOKSHELF_ENTRY_INDEX, "{index}", String.valueOf(index)));
ItemStack itemStack = bookshelfInventory.getItem(counter);
if (itemStack == null) {
builder.append(stringFormatter.getUnFormattedColoredMessage(Formatting.NEUTRAL_BOOKSHELF_EMPTY));
return;
}
ItemMeta meta = itemStack.getItemMeta();
if (meta instanceof BookMeta bookMeta) {
builder.append(getBookDescription(bookMeta));
} else if (meta instanceof EnchantmentStorageMeta enchantmentStorageMeta) {
builder.append(getEnchantedBookDescription(enchantmentStorageMeta));
} else if (meta != null) {
builder.append(stringFormatter.getUnFormattedColoredMessage(
Formatting.NEUTRAL_BOOKSHELF_PLAIN_BOOK_PREFIX)).append(getPlainBookDescription(meta));
}
}
/**
* Gets the description of a plain (enchant-able) book
*
@@ -138,7 +158,8 @@ public class BookshelfListener implements Listener {
private String getPlainBookDescription(@NotNull ItemMeta itemMeta) {
String name = itemMeta.getDisplayName();
if (name.isEmpty()) {
name = "Plain book";
name = BooksWithoutBorders.getStringFormatter().getUnFormattedColoredMessage(
Formatting.NEUTRAL_BOOKSHELF_UNNAMED_PLAIN_BOOK_FORMAT);
}
return name;
}
@@ -153,7 +174,8 @@ public class BookshelfListener implements Listener {
private String getBookDescription(@NotNull BookMeta bookMeta) {
String title = BookHelper.getBookTitle(bookMeta);
String author = BookHelper.getBookAuthor(bookMeta, null);
return ChatColor.of("#686868") + "[Q]" + ChatColor.RESET + title + ChatColor.RESET + " by " + author;
return BooksWithoutBorders.getStringFormatter().replacePlaceholders(Formatting.NEUTRAL_BOOKSHELF_WRITTEN_FORMAT,
List.of("{title}", "{author}"), List.of(title, author));
}
/**
@@ -165,7 +187,8 @@ public class BookshelfListener implements Listener {
@NotNull
private String getEnchantedBookDescription(@NotNull EnchantmentStorageMeta enchantmentStorageMeta) {
StringBuilder builder = new StringBuilder();
builder.append(ChatColor.of("#A64CFF")).append("[E]").append(ChatColor.RESET);
builder.append(BooksWithoutBorders.getStringFormatter().getUnFormattedColoredMessage(
Formatting.NEUTRAL_BOOKSHELF_ENCHANTED_PREFIX));
Map<Enchantment, Integer> enchantmentMap = enchantmentStorageMeta.getStoredEnchants();
List<String> enchantments = new ArrayList<>(enchantmentMap.size());
for (Map.Entry<Enchantment, Integer> enchantmentEntry : enchantmentMap.entrySet()) {

View File

@@ -2,8 +2,10 @@ package net.knarcraft.bookswithoutborders.listener;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.BwBConfig;
import net.knarcraft.bookswithoutborders.config.StaticMessage;
import net.knarcraft.bookswithoutborders.utility.BookLoader;
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper;
import net.knarcraft.knarlib.formatting.StringFormatter;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@@ -31,8 +33,8 @@ public class PlayerEventListener implements Listener {
File file = new File(bookFolder, InputCleaningHelper.cleanString(player.getName()));
if (file.exists()) {
if (!file.renameTo(new File(bookFolder, player.getUniqueId().toString()))) {
BooksWithoutBorders.log(Level.WARNING, "Unable to migrate player book " +
"directory for player " + player.getName());
BooksWithoutBorders.log(Level.WARNING, StringFormatter.replacePlaceholder(
StaticMessage.WARNING_USER_BOOK_MIGRATION_IMPOSSIBLE.toString(), "{player}", player.getName()));
}
}
@@ -59,7 +61,7 @@ public class PlayerEventListener implements Listener {
if (!bookName.trim().isEmpty()) {
//Give the book to the player if it exists
ItemStack newBook = BookLoader.loadBook(player, bookName, "true", "public");
ItemStack newBook = BookLoader.loadBook(player, bookName, true, "public");
if (newBook != null) {
player.getInventory().addItem(newBook);
}

View File

@@ -3,13 +3,16 @@ package net.knarcraft.bookswithoutborders.listener;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.BwBConfig;
import net.knarcraft.bookswithoutborders.config.Permission;
import net.knarcraft.bookswithoutborders.config.translation.SignText;
import net.knarcraft.bookswithoutborders.encryption.EncryptionStyle;
import net.knarcraft.bookswithoutborders.state.BookDirectory;
import net.knarcraft.bookswithoutborders.state.SignType;
import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
import net.knarcraft.bookswithoutborders.utility.BookFormatter;
import net.knarcraft.bookswithoutborders.utility.BookLoader;
import net.knarcraft.bookswithoutborders.utility.EncryptionHelper;
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper;
import net.knarcraft.knarlib.formatting.StringFormatter;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.Material;
import org.bukkit.Tag;
@@ -31,6 +34,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.List;
import static net.knarcraft.bookswithoutborders.utility.BookFileHelper.isBookListIndex;
@@ -48,40 +52,43 @@ public class SignEventListener implements Listener {
String[] lines = event.getLines();
Player player = event.getPlayer();
StringFormatter stringFormatter = BooksWithoutBorders.getStringFormatter();
//Check if creating a Books Without Borders Sign and if the player has permission
if (!lines[0].equalsIgnoreCase("[BwB]") || !player.hasPermission("bookswithoutborders.signs")) {
if (SignType.fromString(lines[0]) != SignType.BOOKS_WITHOUT_BORDERS ||
!player.hasPermission(Permission.SIGNS.toString())) {
return;
}
//Mark the sign as active
event.setLine(0, ChatColor.DARK_GREEN + "[BwB]");
event.setLine(0, stringFormatter.getUnFormattedColoredMessage(SignText.SIGN_HEADER));
//Check if the sign is of a valid type
if ((!lines[1].equalsIgnoreCase("[Encrypt]") && !lines[1].equalsIgnoreCase("[Decrypt]") &&
!lines[1].equalsIgnoreCase("[Give]")) || lines[2].trim().isEmpty()) {
SignType type = SignType.fromString(lines[1]);
if (type == null || lines[2].trim().isEmpty()) {
//Mark the second line as invalid
event.setLine(1, ChatColor.DARK_RED + lines[1]);
player.sendMessage("Invalid sign!");
event.setLine(1, stringFormatter.replacePlaceholder(SignText.SIGN_INVALID, "{line}", lines[1]));
stringFormatter.displayErrorMessage(player, SignText.ERROR_SIGN_INVALID);
return;
}
//Mark the second line as valid
event.setLine(1, ChatColor.DARK_BLUE + lines[1]);
event.setLine(1, stringFormatter.replacePlaceholder(SignText.SIGN_VALID, "{line}", lines[1]));
lines = event.getLines();
//Mark valid encryption/decryption sign
if (lines[1].equalsIgnoreCase(ChatColor.DARK_BLUE + "[Encrypt]") ||
lines[1].equalsIgnoreCase(ChatColor.DARK_BLUE + "[Decrypt]")) {
event.setLine(2, ChatColor.MAGIC + lines[2]);
event.setLine(3, ChatColor.DARK_BLUE + lines[3]);
} else if (lines[1].equalsIgnoreCase(ChatColor.DARK_BLUE + "[Give]")) {
if (type == SignType.GIVE) {
//Generate book giving sign
generateGiveSign(event, lines, player);
} else {
event.setLine(2, stringFormatter.replacePlaceholder(SignText.SIGN_PASSWORD, "{password}", lines[2]));
event.setLine(3, stringFormatter.replacePlaceholder(SignText.SIGN_VALID, "{line}", lines[3]));
}
}
@EventHandler
public void onClick(@NotNull PlayerInteractEvent event) {
StringFormatter stringFormatter = BooksWithoutBorders.getStringFormatter();
Player player = event.getPlayer();
PlayerInventory playerInventory = player.getInventory();
EquipmentSlot hand = event.getHand();
@@ -99,24 +106,24 @@ public class SignEventListener implements Listener {
Tag.WALL_SIGNS.isTagged(event.getClickedBlock().getType())))) {
//The player right-clicked a sign
Sign sign = (Sign) event.getClickedBlock().getState();
if (!signLineEquals(sign, 0, "[BwB]", ChatColor.DARK_GREEN)) {
if (SignType.fromString(sign.getSide(Side.FRONT).getLine(0)) != SignType.BOOKS_WITHOUT_BORDERS) {
return;
}
event.setUseItemInHand(Event.Result.DENY);
event.setCancelled(true);
if (signLineEquals(sign, 1, "[Encrypt]", ChatColor.DARK_BLUE)) {
SignType signType = SignType.fromString(sign.getSide(Side.FRONT).getLine(1));
if (signType == SignType.ENCRYPT) {
encryptHeldBookUsingSign(sign, heldItemType, player, hand);
} else if (signLineEquals(sign, 1, "[Decrypt]", ChatColor.DARK_BLUE)) {
} else if (signType == SignType.DECRYPT) {
decryptHeldBookUsingSign(sign, heldItemType, player, hand);
} else if (signLineEquals(sign, 1, "[Give]", ChatColor.DARK_BLUE) &&
getSignLine2Color(sign) == ChatColor.DARK_GREEN) {
} else if (signType == SignType.GIVE && getSignLine2Color(sign) == ChatColor.DARK_GREEN) {
giveBook(sign, player);
} else {
SignSide front = sign.getSide(Side.FRONT);
player.sendMessage(String.format("Sign command %s %s is invalid", front.getLine(1),
front.getLine(2)));
stringFormatter.displayErrorMessage(player, stringFormatter.replacePlaceholders(SignText.ERROR_SIGN_COMMAND_INVALID,
List.of("{action}", "{data}"), List.of(front.getLine(1), front.getLine(2))));
player.sendMessage(String.valueOf(getSignLine2Color(sign)));
}
} else if (heldItemType == Material.WRITTEN_BOOK && (event.getAction() == Action.LEFT_CLICK_AIR
@@ -177,21 +184,6 @@ public class SignEventListener implements Listener {
}
}
/**
* Checks if a line on a sign equals some string, and that the sign is the correct color
*
* @param sign <p>The sign to read</p>
* @param lineNumber <p>The sign line to read</p>
* @param compareTo <p>The string to look for</p>
* @param color <p>The color to match</p>
* @return <p>True if the given string is what's on the sign</p>
*/
private boolean signLineEquals(@NotNull Sign sign, int lineNumber, @NotNull String compareTo,
@NotNull ChatColor color) {
String line = sign.getSide(Side.FRONT).getLine(lineNumber);
return line.equalsIgnoreCase(color + compareTo);
}
/**
* Changes an edited sign into a sign to give books
*
@@ -284,7 +276,7 @@ public class SignEventListener implements Listener {
}
}
newBook = BookLoader.loadBook(player, fileName, "true", BookDirectory.ENCRYPTED, groupName, heldItem.getAmount());
newBook = BookLoader.loadBook(player, fileName, true, BookDirectory.ENCRYPTED, groupName, heldItem.getAmount());
if (newBook == null) {
BooksWithoutBorders.sendErrorMessage(player, "Unable to load the unencrypted book!");
@@ -341,7 +333,7 @@ public class SignEventListener implements Listener {
fileName += BookFormatter.stripColor(thirdLine);
}
ItemStack newBook = BookLoader.loadBook(player, fileName, "true", "public");
ItemStack newBook = BookLoader.loadBook(player, fileName, true, "public");
if (newBook != null) {
player.getInventory().addItem(newBook);

View File

@@ -0,0 +1,75 @@
package net.knarcraft.bookswithoutborders.state;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.translation.SignText;
import net.knarcraft.bookswithoutborders.utility.BookFormatter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* The type of a BwB sign
*/
public enum SignType {
/**
* The header of a BwB sign
*/
BOOKS_WITHOUT_BORDERS(SignText.SIGN_HEADER),
/**
* A sign for giving a book
*/
GIVE(SignText.SIGN_GIVE),
/**
* A sign for encrypting a book
*/
ENCRYPT(SignText.SIGN_ENCRYPT),
/**
* A sign for decrypting a book
*/
DECRYPT(SignText.SIGN_DECRYPT),
;
private final @NotNull SignText text;
/**
* Instantiates a new sign type
*
* @param text <p>The text designating this sign type</p>
*/
SignType(@NotNull SignText text) {
this.text = text;
}
/**
* Gets the sign type from the given sting
*
* @param input <p>The input to parse</p>
* @return <p>The sign type, or null if not valid</p>
*/
@Nullable
public static SignType fromString(@NotNull String input) {
input = BookFormatter.stripColor(input);
for (SignType signType : SignType.values()) {
if (input.equalsIgnoreCase(getText(signType.text))) {
return signType;
}
}
return null;
}
/**
* Gets the text of the specified sign text
*
* @param signText <p>The sign text to get</p>
* @return <p>The text</p>
*/
@NotNull
private static String getText(@NotNull SignText signText) {
return BookFormatter.stripColor(
BooksWithoutBorders.getStringFormatter().getUnFormattedColoredMessage(signText));
}
}

View File

@@ -2,6 +2,7 @@ package net.knarcraft.bookswithoutborders.utility;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.BwBConfig;
import net.knarcraft.bookswithoutborders.config.Permission;
import net.knarcraft.bookswithoutborders.state.BookDirectory;
import org.bukkit.Material;
import org.bukkit.command.CommandSender;
@@ -34,7 +35,7 @@ public final class BookLoader {
* @return <p>The loaded book</p>
*/
@Nullable
public static ItemStack loadBook(@NotNull CommandSender sender, @NotNull String fileName, @NotNull String isSigned,
public static ItemStack loadBook(@NotNull CommandSender sender, @NotNull String fileName, boolean isSigned,
@NotNull String directory) {
return loadBook(sender, fileName, isSigned, directory, 1);
}
@@ -50,7 +51,7 @@ public final class BookLoader {
* @return <p>The loaded book</p>
*/
@Nullable
public static ItemStack loadBook(@NotNull CommandSender sender, @NotNull String fileName, @NotNull String isSigned,
public static ItemStack loadBook(@NotNull CommandSender sender, @NotNull String fileName, boolean isSigned,
@NotNull String directory, int numCopies) {
BookDirectory bookDirectory = BookDirectory.getFromString(directory);
if (bookDirectory == null) {
@@ -72,7 +73,7 @@ public final class BookLoader {
* @return <p>The loaded book</p>
*/
@Nullable
public static ItemStack loadBook(@NotNull CommandSender sender, @NotNull String fileName, @NotNull String isSigned,
public static ItemStack loadBook(@NotNull CommandSender sender, @NotNull String fileName, boolean isSigned,
@NotNull BookDirectory bookDirectory, @NotNull String directory, int numCopies) {
//Find the filename if a book index is given
try {
@@ -101,7 +102,7 @@ public final class BookLoader {
//Make sure the player can pay for the book
if (config.booksHavePrice() &&
!sender.hasPermission("bookswithoutborders.bypassBookPrice") &&
!sender.hasPermission(Permission.BYPASS_BOOK_PRICE.toString()) &&
(bookDirectory == BookDirectory.PUBLIC || bookDirectory == BookDirectory.PLAYER) &&
config.getEconomyManager().cannotPayForBookPrinting((Player) sender, numCopies)) {
return null;
@@ -140,7 +141,7 @@ public final class BookLoader {
book.setAmount(numCopies);
if (!isSigned.equalsIgnoreCase("true") && book.getItemMeta() != null) {
if (!isSigned && book.getItemMeta() != null) {
return BookHelper.unsignBook((BookMeta) book.getItemMeta(), book.getAmount());
}
return book;

View File

@@ -60,7 +60,6 @@ en:
ERROR_GIVE_NO_RECIPIENT: "You have not specified the recipient of the book!"
ERROR_GIVE_RECIPIENT_UNKNOWN: "Player not found!"
ERROR_GIVE_RECIPIENT_FULL: "Receiving player must have space in their inventory to receive books!"
ERROR_INVALID_COPIES_AMOUNT: "Invalid number of book copies specified!"
ERROR_GIVE_LOAD_FAILED: "Book failed to load!"
ERROR_INVALID_BOOK_PAGE: "Invalid page index given!"
ERROR_OUT_OF_RANGE_BOOK_PAGE: "The given page index is out of bounds!"
@@ -125,9 +124,32 @@ en:
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}]"
NEUTRAL_BOOKSHELF_HEADER_TITLE: "&nBookshelf summary\n&#FF5700Books in {name}&r&#FF5700:{lore}"
NEUTRAL_BOOKSHELF_HEADER_LORE: "\n &d{lore}&r"
NEUTRAL_BOOKSHELF_HEADER_TITLE_EMPTY: "shelf"
NEUTRAL_BOOKSHELF_HEADER_TOP: "\n &#FF5700Top Row:&r"
NEUTRAL_BOOKSHELF_HEADER_BOTTOM: "\n &#FF5700Bottom Row:&r"
NEUTRAL_BOOKSHELF_ENCHANTED_PREFIX: "&#A64CFF[E]&r"
NEUTRAL_BOOKSHELF_WRITTEN_FORMAT: "&#686868[Q]&r{title}&r by {author}"
NEUTRAL_BOOKSHELF_PLAIN_BOOK_PREFIX: "&#A5682A[P]&r"
NEUTRAL_BOOKSHELF_UNNAMED_PLAIN_BOOK_FORMAT: "Plain book"
NEUTRAL_BOOKSHELF_ENTRY_INDEX: "\n &#ffd700{index}. &r"
NEUTRAL_BOOKSHELF_EMPTY: "&7<empty>"
# -----------------------------------------#
# Translations of unknown/untitled author. #
# Altering this might cause problems. #
# -----------------------------------------#
NEUTRAL_UNKNOWN_AUTHOR: "Unknown"
NEUTRAL_UNKNOWN_TITLE: "Untitled"
# ---------------------- #
# Sign text and messages #
# ---------------------- #
SIGN_HEADER: "&2[BwB]"
SIGN_ENCRYPT: "[Encrypt]"
SIGN_DECRYPT: "[Decrypt]"
SIGN_GIVE: "[Give]"
SIGN_PASSWORD: "&k{password}"
SIGN_INVALID: "&4{line}"
SIGN_VALID: "&1{line}"
ERROR_SIGN_INVALID: "Invalid sign!"
ERROR_SIGN_COMMAND_INVALID: "Sign command {action} {data} is invalid"